AI root cause and fixes
How ErrorDock analyses an issue with repository context, how confidence is computed from checkable facts, and the four locks in front of any repository write.
Current availability: Repository writing is not enabled in the current beta setup. The approval and pull-request workflow below describes the implemented capability, not an active beta entitlement. AI analysis requires your own model key.
ErrorDock can read a production error, say why it happened and propose a validated patch. Opening a pull request is switched off in production during the beta: root-cause analysis and fix proposals work today, and the pull-request workflow described further down is built but not enabled.
Symptom System.NullReferenceException in Shop.Api.CartTotals.Apply
Immediate failure cart.Discount.Value was read while Discount was null
Root cause A domain change made Discount optional without updating its readers
Confidence 78 / 100 ← ErrorDock's, computed from checkable evidence
Checks applies ✓ structure ✓ build – tests – secrets ✓ review !
Everything on this page spends more effort on refusing than on producing. That is deliberate. Every other subsystem in ErrorDock is wrong in ways you can see — a query returns the answer or it does not, a percentile is a sketch and says so. A model is wrong in a way that reads exactly like being right: fluent, specific, citing a file and a line number, and completely invented.
Setting it up
AI features run on your own Anthropic or OpenAI key, configured per organisation under Settings → AI. That is the only mode: there is no ErrorDock-billed pool and no shared key. You also set the budget, and every provider request is recorded against it with its token counts and estimated cost.
Three separate grants control what is possible, and they are all off until somebody turns them on:
| Grant | Who |
|---|---|
| Analysis and fix generation | Member, through ai.analysis.run and ai.fix.generate |
| Repository writes | Admin only, through ai.source.write |
| Provider, key, policy and budget | Admin, through settings.update |
Three findings, never one paragraph
Conflating these is the characteristic failure of this whole category of tool, so they are three separate required fields:
| Field | Example |
|---|---|
| Symptom | NullReferenceException — what you already saw |
| Immediate failure | Discount.Value dereferenced while null — the operation and the value |
| Root cause | A domain change made Discount optional without updating its readers |
Only the third tells anybody what to do.
InsufficientEvidence is a first-class outcome with its own storage and its own screen treatment,
recorded together with what would have settled the question. It is not a failure to be retried
until it produces prose.
Evidence is verified before it is stored
Every citation is checked against data ErrorDock already held: a file must have been in the stack trace or actually read, a commit must be one of the deployment's suspects, a runtime value must be in the evidence summary that was sent.
A citation that does not check out is kept but not marked, and does not get to carry the file reference it invented. Keeping it matters — "487 of 503 events" is real evidence that came from the prompt rather than from a file.
The confidence score is ours
model confidence 0.95 ← what the model said about itself. Stored for evaluation. Never shown.
system confidence 78 ← computed from checkable facts. This is what you see.
A model asked how sure it is produces a number correlated with how fluent its answer was. Rendering that would be rendering fluency as certainty. What the screen shows is computed from whether an in-app frame mapped to a file, whether source was actually read, whether the runtime evidence supported the claim, whether a suspect commit exists, and how many occurrences there are.
The score is allowed to disagree with the prose, and it does.
Fix proposals
Nothing in the generation path applies anything. ErrorDock reads the files the analysis cited, asks for edits, applies them in memory, classifies every path, scores the risk, validates, and stores the result.
Edits are search-and-replace, not a diff and not whole files:
| Format | How it fails |
|---|---|
| Unified diff | Line numbers reconstructed from memory, applied by fuzzy matching — the machinery that quietly applies a hunk to the wrong place |
| Whole-file bodies | Costs the entire file in output tokens, and every untouched line is a transcription risk |
| Search-and-replace | Costs only what changed, and fails cleanly: the anchor appears exactly once, or it does not |
An anchor matching twice is refused as ambiguous; an anchor matching zero times is refused as stale. Picking the first match is how a tool edits the wrong overload of a method and produces a patch that compiles and is wrong. Only files the model actually read may be modified, and that is enforced by the applier rather than asked of the prompt.
Path policy
| Classification | Examples |
|---|---|
| Forbidden | .env, *.pem, *.key, *.sql, id_rsa — the redactor stripped their contents, so any edit would be written blind |
| Forbidden | package-lock.json, go.sum, Cargo.lock — written by the resolver that produced them |
| Restricted | .github/workflows/, /infra/, Dockerfile, migrations, package.json — legitimate to change, with review |
| Allowed | Everything else, including tests |
A manifest says what the project wants and is a reasonable thing to change; a lockfile says what a resolver produced and is only ever correct if the resolver wrote it.
Validation
Six stages, each stored as its own row: does it apply, is the structure intact, does it build, do the tests pass, does it introduce a secret, and what does a second model think of it — a reviewer that has not seen the first opinion.
Anything that could not be checked is reported as unavailable, which is not a pass. ErrorDock does not run your test suite: a sandbox executing arbitrary repository code is a different product with a different security review. The build and test stages are configured seams, and they say so rather than pretending to have run.
The four locks on a repository write
| Lock | Owned by | Default |
|---|---|---|
| The installation's repository-writes switch | Whoever runs the server | Off |
| The organisation's pull-request setting | Your administrator | Off |
The ai.source.write permission |
Whoever grants roles — Admin, not Member | — |
| An explicit request carrying an idempotency key | Whoever presses the button | — |
Immediately before the write, every time, ErrorDock rechecks whether the files this patch was written against have moved — and produces the content that will be committed from that check. There is no other way to obtain it, which is what makes the check impossible to skip by accident. A patch whose files moved is marked superseded rather than retried.
The branch name derives from the issue id and nothing that can change:
errordock/fix-3f2a1b4c5d6e
All files go in one commit through the Git Data API, because a patch is a unit: a build that
briefly saw half of it is a build nobody asked for. The commit is attributed to ErrorDock and
records who authorised it — never the other way round. A commit attributed to the developer who
pressed a button is a lie in git blame, in the one place a team most needs the truth.
Nothing merges. There is no merge method, no auto-merge policy and no plan for one. Branch protection is never bypassed; a rejection from a protection rule is reported as blocked, which is your own configuration doing exactly what you set it up to do.
Untrusted input
Everything arriving from your application — exception messages, breadcrumbs, request data — is attacker-influenced text. The defence that matters is the one that does not depend on the model behaving: the applier will only touch files the model actually read, forbidden paths are refused outright, and nothing reaches a repository without a person confirming it.
Quotas
| Limit | Default |
|---|---|
| Analyses per organisation per hour | 60 |
| Fix generations per organisation per hour | 20 |
| Files in a patch before it needs a human decision | 5 |
| Changed lines | 300 |
Audit
Every machine action is attributed to an automation actor, not to a person:
ai.configuration_updated ai.analysis_started ai.analysis_completed
ai.repository_files_read ai.fix_generated ai.fix_validated
ai.branch_created ai.commit_created ai.pull_request_created
ai.repository_files_read records the list of paths, not their contents. The question it
answers is "what of ours left the building"; storing the source would make the audit log a second
copy of the repository.
Deliberately absent
Auto-merge at any risk level. Autonomous remediation. A chat interface over issues. Running your tests. Executing migrations. Fine-tuning or training on your data. "Explain this code" without an issue behind it. GitLab, Bitbucket and Azure DevOps. Background queued analysis.
There is no chat box and no free-text prompt: every model call is one bounded operation with a stored result somebody can read afterwards.
Last updated October 20, 2026