qaas-python 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qaas/adapters/__init__.py +19 -0
- qaas/adapters/tracker.py +1350 -0
- qaas/adapters/vcs.py +494 -0
- qaas/cli.py +1564 -0
- qaas/conductor.py +527 -0
- qaas/config.py +407 -0
- qaas/defaults/config/agents/arbiter.yaml +19 -0
- qaas/defaults/config/agents/cartographer.yaml +20 -0
- qaas/defaults/config/agents/clerk.yaml +21 -0
- qaas/defaults/config/agents/conduit.yaml +19 -0
- qaas/defaults/config/agents/forge.yaml +22 -0
- qaas/defaults/config/agents/mender.yaml +56 -0
- qaas/defaults/config/agents/proof.yaml +21 -0
- qaas/defaults/config/agents/surface.yaml +16 -0
- qaas/defaults/config/system.yaml +69 -0
- qaas/discover.py +227 -0
- qaas/envelope.py +290 -0
- qaas/guardrails.py +431 -0
- qaas/mcp/__init__.py +0 -0
- qaas/mcp/context.py +70 -0
- qaas/mcp/contract_diff.py +937 -0
- qaas/mcp/defect_memory.py +495 -0
- qaas/mcp/env_control.py +905 -0
- qaas/mcp/envelope_server.py +463 -0
- qaas/mcp/test_runner.py +773 -0
- qaas/mcp/tracker.py +412 -0
- qaas/mcp/vcs.py +506 -0
- qaas/paths.py +317 -0
- qaas/plugin/.claude-plugin/plugin.json +9 -0
- qaas/plugin/skills/a11y-audit/SKILL.md +34 -0
- qaas/plugin/skills/adversarial-review/SKILL.md +120 -0
- qaas/plugin/skills/api-surface-extraction/SKILL.md +38 -0
- qaas/plugin/skills/authz-matrix-check/SKILL.md +46 -0
- qaas/plugin/skills/console-error-triage/SKILL.md +39 -0
- qaas/plugin/skills/contract-test-generation/SKILL.md +36 -0
- qaas/plugin/skills/dedupe-strategy/SKILL.md +39 -0
- qaas/plugin/skills/environment-pinning/SKILL.md +35 -0
- qaas/plugin/skills/error-taxonomy/SKILL.md +42 -0
- qaas/plugin/skills/exploratory-ui-walk/SKILL.md +46 -0
- qaas/plugin/skills/failing-test-authoring/SKILL.md +47 -0
- qaas/plugin/skills/flake-detection/SKILL.md +39 -0
- qaas/plugin/skills/form-state-probe/SKILL.md +36 -0
- qaas/plugin/skills/minimal-diff-discipline/SKILL.md +70 -0
- qaas/plugin/skills/openapi-diff/SKILL.md +45 -0
- qaas/plugin/skills/ownership-resolution/SKILL.md +31 -0
- qaas/plugin/skills/product-task-graph/SKILL.md +35 -0
- qaas/plugin/skills/regression-risk-scoring/SKILL.md +59 -0
- qaas/plugin/skills/regression-suite-selection/SKILL.md +36 -0
- qaas/plugin/skills/repo-cartography/SKILL.md +38 -0
- qaas/plugin/skills/repro-minimisation/SKILL.md +41 -0
- qaas/plugin/skills/rollback-plan-authoring/SKILL.md +81 -0
- qaas/plugin/skills/root-cause-vs-symptom/SKILL.md +67 -0
- qaas/plugin/skills/routing-rules/SKILL.md +34 -0
- qaas/plugin/skills/severity-rubric/SKILL.md +42 -0
- qaas/plugin/skills/test-first-fix/SKILL.md +66 -0
- qaas/plugin/skills/test-quality-audit/SKILL.md +58 -0
- qaas/plugin/skills/ticket-writer/SKILL.md +40 -0
- qaas/plugin/skills/verdict-reporting/SKILL.md +35 -0
- qaas/plugin/skills/verification-protocol/SKILL.md +39 -0
- qaas/prompts/ARBITER.md +53 -0
- qaas/prompts/CARTOGRAPHER.md +46 -0
- qaas/prompts/CLERK.md +45 -0
- qaas/prompts/CONDUIT.md +44 -0
- qaas/prompts/FORGE.md +43 -0
- qaas/prompts/MENDER.md +55 -0
- qaas/prompts/PROOF.md +41 -0
- qaas/prompts/SURFACE.md +46 -0
- qaas/prompts/_shared.md +45 -0
- qaas/registry.py +465 -0
- qaas/runner.py +192 -0
- qaas/scorecard.py +425 -0
- qaas/sdk_compat.py +52 -0
- qaas/store.py +290 -0
- qaas/target.py +261 -0
- qaas/tasks.py +361 -0
- qaas/trace.py +270 -0
- qaas_python-0.1.0.dist-info/METADATA +388 -0
- qaas_python-0.1.0.dist-info/RECORD +81 -0
- qaas_python-0.1.0.dist-info/WHEEL +4 -0
- qaas_python-0.1.0.dist-info/entry_points.txt +2 -0
- qaas_python-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dedupe-strategy
|
|
3
|
+
description: >
|
|
4
|
+
Decide whether a finding is already known before it becomes a ticket. TRIGGER
|
|
5
|
+
- run BEFORE creating any ticket, without exception, and whenever the task
|
|
6
|
+
mentions duplicates, 'have we seen this', existing tickets, recurrence,
|
|
7
|
+
regression, or occurrence counts. Do NOT create an issue before completing
|
|
8
|
+
this check; duplicate storms are the single fastest way for a team to stop
|
|
9
|
+
reading anything this system files. SKIP never - if no prior defect matches,
|
|
10
|
+
the check still ran and the answer was no.
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Deduplicating before filing
|
|
14
|
+
|
|
15
|
+
## Order of checks
|
|
16
|
+
|
|
17
|
+
1. **Fingerprint first.** `fingerprint` gives the structural identity: domain, class, service, endpoint, route, file paths with line numbers stripped. An exact match is a duplicate, full stop. The fingerprint deliberately ignores prose, so two agents describing one defect in different words collide by design.
|
|
18
|
+
2. **`search_similar` second.** This catches near-matches the fingerprint misses: same defect after a refactor moved the file, or the same root cause reached through a different endpoint.
|
|
19
|
+
3. **`get_occurrences` on any candidate.** An existing ticket tells you what to do next.
|
|
20
|
+
|
|
21
|
+
## What to do with a match
|
|
22
|
+
|
|
23
|
+
| Situation | Action |
|
|
24
|
+
|---|---|
|
|
25
|
+
| Exact fingerprint, ticket open | Increment occurrence, add the new evidence to the existing ticket. **Do not create.** |
|
|
26
|
+
| Exact fingerprint, ticket **resolved** | This is a **regression**, not a duplicate. File it as one, linked to the original, and say which ticket it regressed. |
|
|
27
|
+
| Near match, same root cause | Add to the existing ticket. Note explicitly why you judged them the same cause. |
|
|
28
|
+
| Near match, different root cause, similar symptom | Two tickets. Link them. Say in each why they are not the same defect. |
|
|
29
|
+
| No match | File it, then `record` it so the next run dedupes against it. |
|
|
30
|
+
|
|
31
|
+
## The judgment call
|
|
32
|
+
|
|
33
|
+
Similarity scores rank candidates; they do not decide. The real question is: **would one fix close both?** If yes, one ticket. If a maintainer would have to make two separate changes, two tickets even when the symptoms look identical.
|
|
34
|
+
|
|
35
|
+
Err toward merging. A merged pair that should have been split costs one comment; a split pair that should have been merged costs two engineers investigating the same bug and finding each other halfway.
|
|
36
|
+
|
|
37
|
+
## Recording
|
|
38
|
+
|
|
39
|
+
`record` is not optional and not a formality. A finding that is filed but not recorded will be filed again next run, by an agent that had no way to know. The occurrence count is also the signal that a defect is getting worse — a count climbing across runs is worth more attention than a single new finding.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: environment-pinning
|
|
3
|
+
description: >
|
|
4
|
+
Pin every variable a reproduction depends on so it runs identically later.
|
|
5
|
+
TRIGGER - read BEFORE reproducing a defect or verifying a fix, and whenever
|
|
6
|
+
the task mentions environment, fixtures, seeding, reset, flags, determinism,
|
|
7
|
+
or 'it works on my machine'. Do NOT run a reproduction against whatever state
|
|
8
|
+
happens to be present. SKIP only when no environment is involved.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Pinning the environment
|
|
12
|
+
|
|
13
|
+
An unpinned reproduction is a story about something that once happened. Pinning is what converts it into a fact anyone can re-check.
|
|
14
|
+
|
|
15
|
+
## Pin all five
|
|
16
|
+
|
|
17
|
+
1. **Branch or commit.** What code.
|
|
18
|
+
2. **Fixture.** Which seeded dataset, by name.
|
|
19
|
+
3. **Flags.** Every feature flag that is set, including the ones you did not change — a default that shifts later silently invalidates the reproduction.
|
|
20
|
+
4. **Role.** Which user, which permissions. "Logged in" is not pinned.
|
|
21
|
+
5. **Clock**, if any behaviour is time-dependent.
|
|
22
|
+
|
|
23
|
+
All five go in the envelope's `environment` block. PROOF will bring up the same environment to verify the fix, and a mismatch there means the verification proves nothing.
|
|
24
|
+
|
|
25
|
+
## Reset between attempts
|
|
26
|
+
|
|
27
|
+
Reset before each reproduction attempt. Residue from the previous attempt is the most common cause of a defect that "sometimes reproduces" — the second run starts from state the first run created, which is a different test.
|
|
28
|
+
|
|
29
|
+
## Seed, do not hand-build
|
|
30
|
+
|
|
31
|
+
Use the named fixture. Data you created by clicking through the UI cannot be recreated exactly by anyone else, including you tomorrow.
|
|
32
|
+
|
|
33
|
+
## Say what you could not pin
|
|
34
|
+
|
|
35
|
+
If the defect depends on something outside your control — a container's startup timing, an external service, wall-clock time of day — say so explicitly in the reproduction. That is not a failure of the work; it is a material fact about the defect, and hiding it wastes the fixer's afternoon.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: error-taxonomy
|
|
3
|
+
description: >
|
|
4
|
+
Audit error responses for a consistent shape, correct status codes, and no
|
|
5
|
+
internal leakage. TRIGGER - read BEFORE reporting anything about error
|
|
6
|
+
handling, and whenever the task mentions error shapes, status codes, error
|
|
7
|
+
responses, stack traces, exception handlers, or 'what happens when it fails'.
|
|
8
|
+
Do NOT judge an error path without triggering it. SKIP only when no error
|
|
9
|
+
behaviour is in scope.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Auditing error behaviour
|
|
13
|
+
|
|
14
|
+
## One shape, everywhere
|
|
15
|
+
|
|
16
|
+
Every error from one API should have the same envelope. Mixed shapes — some endpoints returning `{"error": {"code", "message"}}` and others the framework default `{"detail": "..."}` — force every client into defensive parsing, and clients that skip it break on the paths nobody tested.
|
|
17
|
+
|
|
18
|
+
Check the framework's default handlers specifically. Hand-written handlers are usually consistent; the defaults for validation errors, 404s, and unhandled exceptions are where the second shape leaks in, because nobody wrote them.
|
|
19
|
+
|
|
20
|
+
## Status codes carry meaning
|
|
21
|
+
|
|
22
|
+
| Situation | Code | Common mistake |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| Malformed syntax | 400 | |
|
|
25
|
+
| Well-formed, semantically invalid | 422 | Returned as 400, or as 200 with an error body |
|
|
26
|
+
| Not authenticated | 401 | Confused with 403 |
|
|
27
|
+
| Authenticated, not permitted | 403 | Returned as 404 — sometimes deliberate, to avoid confirming existence |
|
|
28
|
+
| Resource absent | 404 | **Returned as 200 with an empty body** — a silent failure clients cannot detect |
|
|
29
|
+
| Gone permanently | 410 | Correct and deliberate. Not a defect. |
|
|
30
|
+
| Server fault | 500 | Returned for client errors, hiding real faults in the noise |
|
|
31
|
+
|
|
32
|
+
A 200 on a failure is the worst of these. It is undetectable without reading the body, so retries never fire and monitoring stays green.
|
|
33
|
+
|
|
34
|
+
## Leakage
|
|
35
|
+
|
|
36
|
+
An unhandled exception must never return a traceback, file path, library version, SQL fragment, or local variable to a client. Each is a free map of the system for anyone probing it. Findings here are `security_relevant: true`.
|
|
37
|
+
|
|
38
|
+
Check the debug flag's effect too: an app that leaks only when `DEBUG=1` is one environment variable from leaking in production, and that is worth reporting as a configuration risk even where the production value is currently correct.
|
|
39
|
+
|
|
40
|
+
## Trigger the paths
|
|
41
|
+
|
|
42
|
+
Every claim about an error path needs the path actually triggered and the real response captured. Send the malformed body, request the missing id, call it unauthenticated. Reading the handler tells you what it intends; only calling it tells you what the framework does around it.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: exploratory-ui-walk
|
|
3
|
+
description: >
|
|
4
|
+
Drive a product through real user journeys and notice what breaks. TRIGGER -
|
|
5
|
+
read BEFORE opening a browser on a product, and whenever the task mentions
|
|
6
|
+
exploring the UI, walking a journey, clicking through, testing a flow, or 'use
|
|
7
|
+
the app and see what happens'. Do NOT start clicking without a route and a
|
|
8
|
+
reset environment. SKIP only when not driving a browser.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Walking the product
|
|
12
|
+
|
|
13
|
+
## Before the first click
|
|
14
|
+
|
|
15
|
+
Reset the environment and seed it. State left by the previous journey is the single largest source of false findings — a cart with items from the last walk turns a working page into a "defect".
|
|
16
|
+
|
|
17
|
+
Know which task you are performing and what success looks like, from the task graph. Wandering without a goal produces observations; walking a task produces findings.
|
|
18
|
+
|
|
19
|
+
## The loop, every step
|
|
20
|
+
|
|
21
|
+
1. **Read the page.** What is on it, what is interactive, what does it claim.
|
|
22
|
+
2. **Check the console.** Before acting, so you know what was already there.
|
|
23
|
+
3. **Act.** One interaction.
|
|
24
|
+
4. **Observe.** What changed — the page, the URL, the console, the network.
|
|
25
|
+
|
|
26
|
+
Skipping step 2 is how a pre-existing error gets attributed to your click.
|
|
27
|
+
|
|
28
|
+
## Where defects actually live
|
|
29
|
+
|
|
30
|
+
Not on the happy path — someone tested that. They live at:
|
|
31
|
+
|
|
32
|
+
- **Boundaries.** Zero items, one item, many. The single-item case is under-tested precisely because it feels the same as the many case to whoever wrote it.
|
|
33
|
+
- **Second attempts.** Submit, go back, submit again. Refresh mid-flow. Use the browser back button, which nobody tests and every user presses.
|
|
34
|
+
- **Failure branches.** Make validation fail, then look at what happened to the input already typed.
|
|
35
|
+
- **Interruptions.** Navigate away mid-request and back.
|
|
36
|
+
- **Roles.** The same journey as viewer, member, admin.
|
|
37
|
+
|
|
38
|
+
## Use the test ids
|
|
39
|
+
|
|
40
|
+
Elements carry `data-testid`. Use them rather than text or CSS position — text changes, layout shifts, and a walk that breaks on a copy edit is a walk that produces false findings forever.
|
|
41
|
+
|
|
42
|
+
## Judge as a user
|
|
43
|
+
|
|
44
|
+
Report what **fails, misleads, blocks, or excludes**. Not what you would have designed differently. Spacing you dislike, copy you would have written otherwise, a colour choice — none of these are defects, and filing them is how a team learns to ignore this system.
|
|
45
|
+
|
|
46
|
+
A button that does nothing when clicked is the highest-value find available: silent, undetectable by monitoring, and users blame themselves.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: failing-test-authoring
|
|
3
|
+
description: >
|
|
4
|
+
Write the test that proves a defect exists and defines what fixing it means.
|
|
5
|
+
TRIGGER - read BEFORE writing any test that captures a defect, and whenever
|
|
6
|
+
the task mentions a failing test, regression test, acceptance criteria, or
|
|
7
|
+
'write a test for this bug'. Do NOT write the test until the defect reproduces
|
|
8
|
+
reliably. SKIP only when no test is being authored.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Authoring the failing test
|
|
12
|
+
|
|
13
|
+
This single artifact gets used three times: evidence on the ticket, acceptance criterion for the fix, regression test afterwards. Write it for all three readers.
|
|
14
|
+
|
|
15
|
+
## It must fail for the right reason
|
|
16
|
+
|
|
17
|
+
Run it and **read the failure message**. A test that fails because of a typo in a fixture path is not evidence of anything, and it will be discovered by the person trying to fix the defect, who will then distrust the whole ticket.
|
|
18
|
+
|
|
19
|
+
The assertion message should state the contract: `assert "currency" in invoice, "spec marks Invoice.currency required"`.
|
|
20
|
+
|
|
21
|
+
## Assert the contract, never the bug
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
# Right: passes when fixed.
|
|
25
|
+
assert len(response["items"]) <= limit
|
|
26
|
+
|
|
27
|
+
# Wrong: pins the defect in place forever.
|
|
28
|
+
assert len(response["items"]) == 30
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The second passes today, fails when someone fixes the bug, and gets deleted as flaky. It is worse than no test.
|
|
32
|
+
|
|
33
|
+
## One defect, one test
|
|
34
|
+
|
|
35
|
+
A test asserting three things fails on the first and hides the other two. Separate tests, separate names.
|
|
36
|
+
|
|
37
|
+
## Name it for the contract
|
|
38
|
+
|
|
39
|
+
`test_orders_list_respects_limit_parameter` — a reader who sees this go green knows exactly what is now true. `test_api_01` communicates nothing and will not survive a refactor of the ledger.
|
|
40
|
+
|
|
41
|
+
## Self-contained
|
|
42
|
+
|
|
43
|
+
Sets up its own state, does not depend on test ordering, cleans up after itself. A test that only passes as part of a suite, or only after another test ran, is not a reproduction — it is a coincidence.
|
|
44
|
+
|
|
45
|
+
## Never weaken it later
|
|
46
|
+
|
|
47
|
+
The test defines success. If it seems wrong, that is an escalation, not an edit. A fixer who can edit the acceptance criterion has no acceptance criterion — this is exactly the symptom-fix failure the system is built to prevent.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flake-detection
|
|
3
|
+
description: >
|
|
4
|
+
Measure whether a test outcome is deterministic or intermittent before
|
|
5
|
+
trusting it. TRIGGER - read BEFORE recording any reproduction verdict, and
|
|
6
|
+
whenever the task mentions flakiness, intermittent failures, 'sometimes it
|
|
7
|
+
passes', reliability, or repeated runs. Do NOT record a verdict from a single
|
|
8
|
+
run. SKIP only when no verdict is being recorded.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Measuring flake
|
|
12
|
+
|
|
13
|
+
A single run tells you what happened once. Reproduction verdicts require knowing what happens repeatedly, and this is the check that separates a real defect from a coincidence.
|
|
14
|
+
|
|
15
|
+
## The measurement
|
|
16
|
+
|
|
17
|
+
Run the test N times (the run's `flake_runs`, default 5) with `run_n_times`, unchanged, in the same environment. The flake rate is the share of runs whose outcome differs from the majority.
|
|
18
|
+
|
|
19
|
+
- **0.0** — deterministic. Verdict `reproduced` (or the finding is genuinely absent).
|
|
20
|
+
- **Anything above 0.0** — verdict `flaky`. Record the actual rate; do not round it to zero because it "mostly fails".
|
|
21
|
+
|
|
22
|
+
## Flaky is its own verdict
|
|
23
|
+
|
|
24
|
+
A flaky defect is not a reproduced defect with a caveat. It goes to the quarantine queue, not into the normal ticket flow, because an intermittent failing test poisons a fix cycle: the fixer sees it pass, closes the ticket, and the defect ships.
|
|
25
|
+
|
|
26
|
+
Recording 0.0 for something that failed four times in five is the single most damaging thing to get wrong here. It converts a quarantine item into a confident ticket and an acceptance criterion nobody can rely on.
|
|
27
|
+
|
|
28
|
+
## Sources of flake, worth distinguishing
|
|
29
|
+
|
|
30
|
+
- **Timing** — races, unawaited work, fixed sleeps. Usually a real defect wearing a flake costume.
|
|
31
|
+
- **Shared state** — a previous run's data. Usually a fixture problem, not a product problem.
|
|
32
|
+
- **Clock or ordering dependence** — real defects that only surface under specific conditions.
|
|
33
|
+
- **The environment** — a slow container, a cold cache. Not the product's fault, and worth saying so.
|
|
34
|
+
|
|
35
|
+
Say which you believe it is, and say that you are inferring. The distinction changes who picks the ticket up.
|
|
36
|
+
|
|
37
|
+
## More runs when it matters
|
|
38
|
+
|
|
39
|
+
Five runs cannot distinguish 0.05 from 0.0. For a defect that would be blocker or critical if real, run more before recording a confident verdict — the cost of the extra runs is trivial against the cost of a wrong verdict either way.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: form-state-probe
|
|
3
|
+
description: >
|
|
4
|
+
Test what a form does when validation fails, when submission fails, and when
|
|
5
|
+
the user comes back. TRIGGER - read BEFORE testing any form, and whenever the
|
|
6
|
+
task mentions forms, validation, input handling, submit behaviour, or lost
|
|
7
|
+
input. Do NOT test only the successful submission - the failure branches are
|
|
8
|
+
where the defects are. SKIP only when no form is in scope.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Probing a form
|
|
12
|
+
|
|
13
|
+
The successful submission is tested. The failure branches are not, and that is where the defects concentrate.
|
|
14
|
+
|
|
15
|
+
## The sequence
|
|
16
|
+
|
|
17
|
+
1. **Submit empty.** Does validation fire at all?
|
|
18
|
+
2. **Fill everything correctly except one field.** Submit. Then — the critical check — **is everything else still there?**
|
|
19
|
+
3. **Fix the one field.** Submit. Does it now succeed?
|
|
20
|
+
4. **Make the server reject it** (duplicate value, stale resource). Is the input preserved this time too?
|
|
21
|
+
5. **Submit twice quickly.** One resource, or two?
|
|
22
|
+
6. **Navigate away and back.** Draft preserved, or silently gone?
|
|
23
|
+
|
|
24
|
+
## Lost input is the big one
|
|
25
|
+
|
|
26
|
+
Step 2 catches the most damaging form defect there is. A validation-failure branch that resets state to its initial value means a user who mistypes one field loses everything they entered. It is easy to write by accident, invisible to the developer who tests with two fields, and infuriating on a form with twelve.
|
|
27
|
+
|
|
28
|
+
Distinguish carefully: a form that clears on **validation failure** is a defect. A form that clears on **successful submission** is correct. Check which branch you actually triggered — reporting the second as the first is a false positive that undermines the real finding.
|
|
29
|
+
|
|
30
|
+
## Also worth checking
|
|
31
|
+
|
|
32
|
+
- **Validation timing.** Errors on every keystroke before the user has finished typing are their own defect.
|
|
33
|
+
- **Error placement.** An error at the top of a long form, with no indication of which field, is barely better than none.
|
|
34
|
+
- **Announcement.** Is the error reachable by a screen reader? See `a11y-audit`.
|
|
35
|
+
- **Double submit.** No disabled state during submission means duplicate resources.
|
|
36
|
+
- **Trimming.** Does a trailing space defeat validation?
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: minimal-diff-discipline
|
|
3
|
+
description: >
|
|
4
|
+
Decide what belongs in this diff and what is a separate ticket, and what to do
|
|
5
|
+
when the correct fix exceeds the budget. TRIGGER - read BEFORE the first edit
|
|
6
|
+
of a fix and again before opening the pull request, and whenever the task
|
|
7
|
+
mentions diff size, scope, the autonomy envelope, refactoring, 'while I was in
|
|
8
|
+
there', or a file limit refusal. Do NOT carry an improvement along with a bug
|
|
9
|
+
fix because it is obviously better. SKIP only when no diff is being produced.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Keeping the diff minimal
|
|
13
|
+
|
|
14
|
+
Every line beyond the fix is a line a reviewer has to judge and a line that can break something. The budget is not a target to fill; most good fixes are one file.
|
|
15
|
+
|
|
16
|
+
## The budget is real and it is enforced
|
|
17
|
+
|
|
18
|
+
Your policy sets `max_diff_files: 5` and `max_diff_lines: 150` (§8.2).
|
|
19
|
+
|
|
20
|
+
- **Files are counted and enforced** by the PreToolUse hook, per *distinct* file, not per call — editing one file six times is one file. On the sixth distinct file the write is refused with: *"MENDER has already changed 5 files, which is its limit of 5 (§8.2). A fix this wide is outside the autonomy envelope: stop, and escalate with what you have found."* The refusal names the files already touched. It is not a rate limit to wait out; it is the envelope closing.
|
|
21
|
+
- **Lines are a declared budget, not a machine check.** Nothing stops you at 150. You hold it, and ARBITER measures it. Treat crossing it exactly as you would treat the file refusal.
|
|
22
|
+
- **Forbidden classes stop you whatever the size.** Migrations, anything matching `*auth*`, payment, billing, secrets, `*.tf`, `*/infra/*`, Docker and `.github` are refused with the class named: *"...is outside MENDER's autonomy envelope: it is a database migration. Changes here need human approval (§8.2). Describe the change you would make and escalate instead of making it."* Do that literally — describe the change, do not find a path that misses the glob.
|
|
23
|
+
|
|
24
|
+
## What belongs in this diff
|
|
25
|
+
|
|
26
|
+
| What you are holding | In the diff? | Why |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| The line whose behaviour causes the defining test to fail | Yes | It is the fix |
|
|
29
|
+
| An import, constant or helper the fix requires | Yes | Not optional, not scope |
|
|
30
|
+
| The regression test | Yes | Part of the fix, not an extra |
|
|
31
|
+
| A second call site with the *same* root cause and the *same* one-line change | Yes — and say so in the PR body | Fixing one instance of a shared cause leaves the defect live |
|
|
32
|
+
| A different bug you noticed while reading | No | Name it in the PR body and your final report. You may not create tickets; `create_issue` is refused for you |
|
|
33
|
+
| A rename, extraction, dead-code removal, formatting, type hints | No | Separate ticket, however obviously correct |
|
|
34
|
+
| A debug print or log line you added while tracing | No | Delete it before you commit |
|
|
35
|
+
| A dependency bump | No | Its blast radius is not this ticket's |
|
|
36
|
+
| A test that was already failing on the base | No | Report it; fixing it hides which change fixed what |
|
|
37
|
+
| Anything under a forbidden path | No | Escalate with the change you would have made |
|
|
38
|
+
|
|
39
|
+
## Neighbouring correct code shows you the shape of the fix
|
|
40
|
+
|
|
41
|
+
The strongest signal for what a minimal fix looks like is code beside it that already gets it right.
|
|
42
|
+
|
|
43
|
+
1. Find the sibling. Grep the file and the module for the same decorator, the same route pattern, the same call to the collaborator that broke — a handler two functions down, the other branch of the same switch, the sibling serializer.
|
|
44
|
+
2. Read both, and diff them by eye. List every difference: a validation call, a tenant filter, an `await`, a default, an ordering.
|
|
45
|
+
3. The fix is usually one of those differences, restored. Prefer *making the broken one look like the working one* to inventing a mechanism neither of them uses.
|
|
46
|
+
4. Verify the neighbour is actually correct before you copy it. A pattern repeated twice may be the bug twice. Check it against the contract, not against its popularity.
|
|
47
|
+
|
|
48
|
+
Two things fall out of this for free: the diff stays small, and the reviewer can check it by reading the neighbour rather than reconstructing your reasoning.
|
|
49
|
+
|
|
50
|
+
## Refactoring carried along is scope creep
|
|
51
|
+
|
|
52
|
+
It is scope creep even when the refactor is genuinely better, even when it is smaller than the fix, and even when the fix would be cleaner after it. The reason is not aesthetics: a reviewer cannot tell which hunk changed behaviour and which only moved it, so the risk of the whole diff becomes the risk of its largest hunk. A revert then reverts the refactor too, which is exactly when you least want to be reading a large patch.
|
|
53
|
+
|
|
54
|
+
The test: **if the refactor were reverted, would the defining test still pass?** If yes, it does not belong here.
|
|
55
|
+
|
|
56
|
+
## When the correct fix genuinely exceeds the budget
|
|
57
|
+
|
|
58
|
+
This is a real and useful outcome. The budget is a statement about what a review can reliably bound, so exceeding it is a finding about the defect, not an obstacle.
|
|
59
|
+
|
|
60
|
+
Do **not** slice. Three PRs of four files each, or a fix landed in halves across two tickets, defeats the control entirely: each slice is individually unreviewable, none of them fixes the defect, and the risk a human was supposed to see never gets seen.
|
|
61
|
+
|
|
62
|
+
Escalate with the real scope, specific enough that an engineer can act on it without repeating your work:
|
|
63
|
+
|
|
64
|
+
- The full file list and an honest line estimate.
|
|
65
|
+
- Why each file is required — one clause each.
|
|
66
|
+
- The root cause, stated once, plainly.
|
|
67
|
+
- What blocks the small version. "It cannot be done in one file because the tenant id is not threaded past the repository layer" is useful; "this is complex" is not.
|
|
68
|
+
- Whether a forbidden class is involved, named.
|
|
69
|
+
|
|
70
|
+
"The defect is real and reproduces, but fixing it properly means changing the session model, which is outside my envelope" saves an engineer an hour. A plausible-looking change that does not fix the defect costs them a day, and costs this system their trust.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: openapi-diff
|
|
3
|
+
description: >
|
|
4
|
+
Compare a declared API spec against what the implementation actually does, and
|
|
5
|
+
classify each difference by consumer impact. TRIGGER - read BEFORE reporting
|
|
6
|
+
any spec drift or breaking change, and whenever the task mentions OpenAPI, the
|
|
7
|
+
spec, contract drift, breaking changes, schema comparison, or 'does the API
|
|
8
|
+
match the docs'. Do NOT report a textual difference as a defect without
|
|
9
|
+
classifying its impact. SKIP only when no spec comparison is involved.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Diffing spec against implementation
|
|
13
|
+
|
|
14
|
+
## Breaking is defined from the consumer's side
|
|
15
|
+
|
|
16
|
+
The only question that matters: **would a client written against the old contract break against the new behaviour?** Not whether the change is large, or intentional, or an improvement.
|
|
17
|
+
|
|
18
|
+
| Change | Verdict | Why |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| Response field removed | **Breaking** | A client reads it and gets `undefined` |
|
|
21
|
+
| Response field made optional / nullable | **Breaking** | Same failure, arriving intermittently, which is worse |
|
|
22
|
+
| Required request field added | **Breaking** | Every existing call now 422s |
|
|
23
|
+
| Status code changed | **Breaking** | Clients branch on status |
|
|
24
|
+
| Enum value removed | **Breaking** | A client that sends it now fails |
|
|
25
|
+
| Type narrowed (`string` to `enum`, `int` to `int>=1`) | **Breaking** | Previously valid input rejected |
|
|
26
|
+
| Endpoint removed | **Breaking** | Obviously |
|
|
27
|
+
| Optional response field added | Non-breaking | Ignored by old clients |
|
|
28
|
+
| Enum value added to a **response** | Breaking-ish — flag it | Clients with exhaustive switches fail |
|
|
29
|
+
| Optional request field added | Non-breaking | |
|
|
30
|
+
| Description or example changed | Not a change | Do not report it |
|
|
31
|
+
|
|
32
|
+
## Direction matters
|
|
33
|
+
|
|
34
|
+
"In the spec, absent from the implementation" and "in the implementation, absent from the spec" are different defects with different fixes:
|
|
35
|
+
|
|
36
|
+
- **Spec has it, code does not** — a broken promise. Anyone reading the docs writes code that fails. Usually the more severe of the two.
|
|
37
|
+
- **Code has it, spec does not** — undocumented surface. Nobody depends on it deliberately, but nothing protects it either, and it is often unmaintained and unguarded.
|
|
38
|
+
|
|
39
|
+
Say which direction you found. A report that only says "currency mismatch" leaves the reader to work out who is wrong.
|
|
40
|
+
|
|
41
|
+
## Prove it
|
|
42
|
+
|
|
43
|
+
A structural diff is a hypothesis. Call the endpoint, capture the real response, and attach it. A diff between two files is evidence about files; a captured response is evidence about the system.
|
|
44
|
+
|
|
45
|
+
Then generate the contract test. A finding that ships with a test failing today and passing when fixed is a finding nobody has to argue about.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ownership-resolution
|
|
3
|
+
description: >
|
|
4
|
+
Resolve a file path or component to an owning team. TRIGGER - read BEFORE
|
|
5
|
+
assigning a ticket, before recording the ownership section of a system map,
|
|
6
|
+
and whenever the task mentions owners, teams, assignees, CODEOWNERS, or 'who
|
|
7
|
+
should fix this'. Do NOT guess a team from a directory name; an unassigned
|
|
8
|
+
ticket beats a misrouted one. SKIP only when nothing is being assigned.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Resolving ownership
|
|
12
|
+
|
|
13
|
+
## Source of truth, in order
|
|
14
|
+
|
|
15
|
+
1. `CODEOWNERS` — the only authoritative source when it exists.
|
|
16
|
+
2. An explicit ownership manifest (`OWNERS`, service catalog, team annotations).
|
|
17
|
+
3. Nothing else. Not directory names, not commit history, not who last touched the file.
|
|
18
|
+
|
|
19
|
+
Commit history is a specific trap: the person who last edited a file is often the person who fixed someone else's bug in it, and routing to them is a small tax on being helpful.
|
|
20
|
+
|
|
21
|
+
## Matching
|
|
22
|
+
|
|
23
|
+
`CODEOWNERS` patterns are last-match-wins in most implementations — a later rule overrides an earlier one for the same path. Walk the file in order and keep the last match, not the first. Glob patterns match path segments; `web/src/routes/Orders*` covers `OrdersList.tsx` but not `web/src/components/OrderRow.tsx`.
|
|
24
|
+
|
|
25
|
+
For a finding spanning several files, resolve each and take the owner of the file where the **fix** most likely lands, not where the symptom appears. A UI symptom caused by an API defect belongs to the API team.
|
|
26
|
+
|
|
27
|
+
## When there is no owner
|
|
28
|
+
|
|
29
|
+
Record `null`, assign nothing, and say so explicitly in the ticket: "No owner recorded in CODEOWNERS for this path."
|
|
30
|
+
|
|
31
|
+
That sentence is doing real work. It tells a human exactly what to fix, and it converts a silent misroute into a visible gap in the ownership file. Guessing produces a ticket that sits in the wrong queue until someone notices, and it teaches the team that assignments from this system cannot be trusted.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: product-task-graph
|
|
3
|
+
description: >
|
|
4
|
+
Build or read the graph of user-facing tasks: what a person comes to this
|
|
5
|
+
product to do and which routes and actions each task needs. TRIGGER - read
|
|
6
|
+
BEFORE recording a task graph or planning UI exploration, and whenever the
|
|
7
|
+
task mentions user journeys, flows, task graph, critical paths, or 'what can a
|
|
8
|
+
user do here'. Do NOT derive journeys from the route list alone - routes are
|
|
9
|
+
not tasks. SKIP only when following an existing task graph step by step.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# The product task graph
|
|
13
|
+
|
|
14
|
+
A route inventory says what pages exist. A task graph says **what someone is trying to accomplish**, and those are different objects. `/checkout/review` is a route; "place an order" is a task that happens to pass through it.
|
|
15
|
+
|
|
16
|
+
## Building one
|
|
17
|
+
|
|
18
|
+
For each task record: the goal in a user's words, the entry point, the ordered steps, the success condition, and what the user needs to already have (an account, a populated cart, a specific role).
|
|
19
|
+
|
|
20
|
+
Derive tasks from what the product is *for* — nouns in the domain model and verbs in the UI copy — then check each against the routes. Two useful signals fall out:
|
|
21
|
+
|
|
22
|
+
- A route no task passes through is **orphaned**. Either a task is missing from the graph or the route is dead. Both are worth reporting.
|
|
23
|
+
- A task with no complete route path is **broken or unimplemented**, and that is a finding on its own.
|
|
24
|
+
|
|
25
|
+
## Prerequisites are the valuable part
|
|
26
|
+
|
|
27
|
+
Most journey testing fails at setup, not at the step under test. Record precisely what state each task needs before its first step: which role, what seeded data, which flags. A task graph without prerequisites sends an explorer into a login wall and produces a false finding about a page that was working.
|
|
28
|
+
|
|
29
|
+
## Include the unhappy paths
|
|
30
|
+
|
|
31
|
+
The graph is not just the golden path. For each task, record what should happen when it fails: validation rejects the input, the network drops, the session expires, the resource is gone. Those branches are where defects concentrate, precisely because nobody writes tests for them.
|
|
32
|
+
|
|
33
|
+
## Rank by consequence
|
|
34
|
+
|
|
35
|
+
Mark which tasks are critical — the ones where failure means lost revenue, lost data, or a user who cannot recover. Time-boxed runs walk those first, and "we ran out of budget before checkout" is a much worse outcome than "we ran out of budget before the settings page".
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: regression-risk-scoring
|
|
3
|
+
description: >
|
|
4
|
+
Grade a change's blast radius low, medium or high, and turn that grade into a
|
|
5
|
+
verdict. TRIGGER - read BEFORE recording a review decision on any diff, and
|
|
6
|
+
whenever the task mentions blast radius, regression risk, breaking changes,
|
|
7
|
+
call sites, contracts or schemas, response shape, status codes, shared state,
|
|
8
|
+
or concurrency. Do NOT infer risk from the size of the diff; a one-line change
|
|
9
|
+
to a shared serializer outranks forty lines in a leaf module. SKIP only when no
|
|
10
|
+
change is being judged for risk.
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Scoring regression risk
|
|
14
|
+
|
|
15
|
+
Risk here means: **if this change is wrong, how much breaks, and how far from the diff?** It is independent of the defect's severity and independent of the diff's size. Score it before you decide, because the grade is what turns "I found nothing wrong" into a defensible verdict.
|
|
16
|
+
|
|
17
|
+
## The seven inputs
|
|
18
|
+
|
|
19
|
+
Work through all seven. Each has a way to measure it with the tools you have — reading and guessing is where reviews get this wrong.
|
|
20
|
+
|
|
21
|
+
| Input | How to measure it | Raises risk when |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| **Call sites** | Grep the changed symbol across the repo. For an endpoint, `find_consumers` (heuristic: it matches the literal path and the stable prefix, so a client assembling URLs from fragments is missed — a low count is weak evidence) | Many callers, or callers outside the changed module, or callers not covered by the tests that were run |
|
|
24
|
+
| **Contract and schema** | `diff_openapi` when the change touches an endpoint, then `classify_breaking` on each change it returns | Any change classified `breaking`. `unknown` is **not** `non_breaking` — it means the server has no rule, so judge it by hand and grade at least medium |
|
|
25
|
+
| **Response shape** | Read the handler's return and the model it serialises | A field removed or renamed, a type changed, nullability changed, ordering that a client relies on. Additive fields are usually safe; removals never are |
|
|
26
|
+
| **Status codes** | The handler's raises and returns, and the spec's declared responses | A code changed at all. Clients branch on status far more than on body |
|
|
27
|
+
| **Shared state** | Look for module-level values, caches, singletons, session or request-scoped globals, connection pools, class attributes | The change reads or writes anything a second request can also see |
|
|
28
|
+
| **Concurrency** | Async handlers, background tasks, locks, retries, anything with an implicit ordering assumption | The change alters ordering, adds a shared mutable, or replaces a race with a retry |
|
|
29
|
+
| **Persistence and irreversibility** | The rollback note, checked against the diff — see `rollback-plan-authoring` | The change writes data, mutates rows, or poisons a cache, so a revert does not restore the prior state |
|
|
30
|
+
|
|
31
|
+
Two more that override the grade rather than contribute to it:
|
|
32
|
+
|
|
33
|
+
- **A forbidden class in the diff** — a migration, anything auth, payment, billing, secrets, `*.tf`, infra, Docker, `.github`. These are refused at the guardrail, so their presence means the envelope was routed around. That is an immediate `ESCALATE_TO_HUMAN` regardless of everything else.
|
|
34
|
+
- **The critical journey.** A low-risk change on the checkout path still fails users if it is wrong. It does not raise the grade; it lowers your tolerance for an unresolved concern.
|
|
35
|
+
|
|
36
|
+
## The grades
|
|
37
|
+
|
|
38
|
+
| Grade | Looks like | What it implies for the verdict |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| **Low** | One function, private or module-local; every caller either in the diff or provably unaffected; no contract, schema, status or response-shape change; no shared state; read-path only; fully reversible | `APPROVE` when the fix is correct and minimal. Note any residual concern and move on |
|
|
41
|
+
| **Medium** | A shared helper or a module with several callers; an additive response field; a touched endpoint whose spec diff is `non_breaking`; writes data that a revert leaves behind but that is describable; two or more low markers together | `APPROVE` only with the callers' tests actually run, a viable rollback note, and named `concerns`. Otherwise `REQUEST_CHANGES` naming the specific gap — most often "the callers in \<file\> were not exercised" |
|
|
42
|
+
| **High** | Any `breaking` classification; a status code change; a removed or renamed response field; shared mutable state or concurrency; an irreversible effect with no mitigation; a forbidden class; or three or more medium markers | `REQUEST_CHANGES` when the gap is fixable within MENDER's envelope, otherwise `ESCALATE_TO_HUMAN`. A two-round-trip loop is not the right place to absorb high risk |
|
|
43
|
+
|
|
44
|
+
## How the grade meets the fix
|
|
45
|
+
|
|
46
|
+
Risk and correctness are separate axes and combining them is the point:
|
|
47
|
+
|
|
48
|
+
- **Correct fix, low risk** → approve.
|
|
49
|
+
- **Correct fix, high risk** → the change may still be right and still not be something this loop should land unattended. Escalate with the risk named; do not send it back for cosmetic changes that will not lower it.
|
|
50
|
+
- **Uncertain fix, low risk** → the cheapest resolution is usually a specific `REQUEST_CHANGES` asking for the missing test, not a debate.
|
|
51
|
+
- **Uncertain fix, high risk** → escalate. Two agents guessing at a high-risk change is exactly the case §8.4 reserves for a human.
|
|
52
|
+
|
|
53
|
+
## Traps
|
|
54
|
+
|
|
55
|
+
- **Diff size is not risk.** Score the reach, not the line count. The most dangerous diffs in this system are one line long.
|
|
56
|
+
- **Severity is not risk.** A blocker can have a low-risk fix; a minor cosmetic ticket can be fixed by editing a shared component. They are scored on different axes by different skills — severity by `severity-rubric`, risk here.
|
|
57
|
+
- **A heuristic's silence is not safety.** `find_consumers` and `affected_tests` both document themselves as heuristics. "No consumers found" means the search found none, and should lower your confidence in the search before it lowers the grade.
|
|
58
|
+
- **Do not grade from the PR body.** The author's account of the blast radius is the claim under review.
|
|
59
|
+
- **Say the grade out loud.** Put it in `record_review`'s `reasoning` with the two or three inputs that decided it. A grade nobody can audit is not a control, and PROOF reads your reasoning when it selects the regression suite.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: regression-suite-selection
|
|
3
|
+
description: >
|
|
4
|
+
Choose which tests to run after a change, so verification is fast and still
|
|
5
|
+
catches breakage. TRIGGER - read BEFORE selecting tests to run for a
|
|
6
|
+
verification, and whenever the task mentions regression suite, affected tests,
|
|
7
|
+
which tests to run, or test selection. Do NOT run the entire suite by default
|
|
8
|
+
and do NOT run only the one test that was failing. SKIP only when no test
|
|
9
|
+
selection is being made.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Selecting the regression suite
|
|
13
|
+
|
|
14
|
+
Two failure modes bracket this, and both are common. Running everything is slow enough that verification gets skipped under time pressure. Running only the original failing test catches nothing — that test passing is the *premise* of the check, not the check.
|
|
15
|
+
|
|
16
|
+
## Build the set
|
|
17
|
+
|
|
18
|
+
1. **The original failing test.** Always. Already run in step 2 of the protocol.
|
|
19
|
+
2. **`affected_tests` on the diff.** Direct coverage of changed files.
|
|
20
|
+
3. **Tests for the changed module's callers.** A fix inside a shared helper breaks its consumers, not itself.
|
|
21
|
+
4. **Contract tests for any touched endpoint.** A fix that changes a response shape is a breaking change wearing a bugfix label.
|
|
22
|
+
5. **The journey the defect lived on**, end to end, if UI or realtime was involved.
|
|
23
|
+
|
|
24
|
+
## Widen when the diff is risky
|
|
25
|
+
|
|
26
|
+
Shared utility, auth, serialisation, or anything under a migration — widen substantially. A narrow selection over a wide blast radius is a verification that proves almost nothing, and it looks identical in the ledger to one that proves a lot.
|
|
27
|
+
|
|
28
|
+
## Pre-existing failures
|
|
29
|
+
|
|
30
|
+
Some tests were already failing before the fix. **Establish that before you start**, on the base commit, or you will attribute someone else's breakage to this change and reopen a correct fix.
|
|
31
|
+
|
|
32
|
+
Report them explicitly: "the suite passed except two failures also present on the base commit, unrelated to this change." Silently ignoring them is how a real regression hides among the known noise.
|
|
33
|
+
|
|
34
|
+
## Say what you did not run
|
|
35
|
+
|
|
36
|
+
A verdict that names its own coverage is trustworthy. "VERIFIED — original test passes, 34 affected tests pass, did not run the load suite" lets a reader judge the residual risk. A bare "VERIFIED" invites them to assume you checked more than you did.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: repo-cartography
|
|
3
|
+
description: >
|
|
4
|
+
Map a repository into services, modules and dependency edges. TRIGGER - read
|
|
5
|
+
BEFORE exploring an unfamiliar repository or publishing any system map, and
|
|
6
|
+
whenever the task mentions mapping, the system map, services, module
|
|
7
|
+
structure, dependency graph, or 'what is in this codebase'. Do NOT start
|
|
8
|
+
opening files at random; the order of exploration is the whole technique. SKIP
|
|
9
|
+
only when a current map already exists and you are reading it rather than
|
|
10
|
+
building it.
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Mapping a repository
|
|
14
|
+
|
|
15
|
+
## Order matters
|
|
16
|
+
|
|
17
|
+
Breadth before depth, always. The failure mode is opening `main.py`, following an import, and forty minutes later knowing one code path deeply and the repository not at all.
|
|
18
|
+
|
|
19
|
+
1. **Shape first.** Directory listing at depth 2-3. Manifests (`pyproject.toml`, `package.json`, `go.mod`) mark service boundaries better than any convention.
|
|
20
|
+
2. **Entry points.** One per service. Where does the process start, what does it bind, what does it mount?
|
|
21
|
+
3. **Declared contracts.** OpenAPI specs, schema files, migrations, router configs. These are dense and authoritative — one file often yields twenty routes.
|
|
22
|
+
4. **Only then, handlers.** And only enough to answer specific questions the contracts left open.
|
|
23
|
+
|
|
24
|
+
## Record the code, not the documentation
|
|
25
|
+
|
|
26
|
+
Where a comment, a README, or a spec disagrees with the implementation, **the implementation is what you record** — and the disagreement itself goes in `drift`, because it is a finding another agent will act on. Quietly preferring one side destroys information.
|
|
27
|
+
|
|
28
|
+
## Missing is `null`
|
|
29
|
+
|
|
30
|
+
A guessed field is worse than an absent one, because everything downstream trusts the map without re-deriving it. An empty list means "none exist"; `null` means "not determined". Keep them distinct — they lead to different downstream behaviour.
|
|
31
|
+
|
|
32
|
+
## Dependency edges
|
|
33
|
+
|
|
34
|
+
Record only edges that matter for reasoning about coupling: cross-service calls, cross-layer imports, shared database tables. A complete import graph of every module is noise that makes cycles harder to see, not easier.
|
|
35
|
+
|
|
36
|
+
## Cost
|
|
37
|
+
|
|
38
|
+
You are the cheapest agent per unit of value in the system, because every other agent stops re-deriving this. Spend turns on coverage. An incomplete map is not a partial success — it silently caps the quality of every discovery agent that reads it.
|