Restructure SKILL.md as a gated procedure, not advice
Why
The reference material was good and mostly went unread in practice. The failure mode was
not ignorance - it was reading the file, agreeing with it, and then reasoning from first
principles anyway, because everything in it was phrased as guidance. Guidance can be
acknowledged and set aside; a gate cannot be cleared without doing the thing.
SKILL.md is now a procedure
New top matter, in the order a reader meets it:
- "How to use this file" states plainly that this is a procedure with gates, that this
file wins over intuition until evidence overrides it, and why (every rule is residue
of a failure that cost hours).
- Four override rules R1-R4, each with a one-line claim, the concrete trap, and a
pointer. R1 deliverable-as-a-testable-sentence, R2 one variable plus a control build,
R3 never ship or claim an unverified artifact, R4 identify the owning layer and
re-classify when reality disagrees.
- The symptom index is retitled "a matching row is a stop signal" and now carries an
explicit instruction: load the file before your next command, not after your next
three attempts.
- Four gates G1-G4, each an action with a pass criterion and a fail description:
deliverable form, environment truth (doctor.py + preflight), code location (including
the virtualization check even when recon says "no packer"), and baseline/control with
recorded time-to-death.
- The workflow gains a skip-condition line per step ("it seems unnecessary" is not a
condition) and a two-strike rule: two failures of the same shape mean the model is
wrong, not the parameters, so re-classify instead of running a third variant.
- New "What done means": six items, all required, with an explicit rule that a clean log
is not evidence and that anything short of six is a checkpoint to be labelled as one.
- New "Stop conditions": the six moments where pushing forward is the wrong move -
repeated same-shape failure, a no-effect patch about to be retried, an unexplained
failure, an unproven "blocked" verdict, a success claimed from absence of errors, and
a measurement that disagrees with a settled conclusion.
README
Documents the gated design and why it exists, and tells an agent reading it what to act
on rather than read: the four rules, the symptom index as a stop signal, the gates, the
two-strike rule, and the definition of done. Points at scripts/doctor.py as the cheapest
first command.
Not changed
Three section references (`ad-removal.md` server-driven, `recon.md` code-location,
`dex-patching.md` finding-the-call-site) do not match a `##` heading textually. Each
target does exist - two are bolded inline subheadings and one is "## Finding the call
site" - so they resolve for a reader. Left alone deliberately: rewriting them would add
churn without changing behaviour.
This commit is contained in:
@@ -9,6 +9,34 @@ organized for progressive disclosure — a short decision-oriented `SKILL.md`, d
|
|||||||
references loaded only when a step needs them, and parameterized scripts you can run
|
references loaded only when a step needs them, and parameterized scripts you can run
|
||||||
directly.
|
directly.
|
||||||
|
|
||||||
|
## How an agent is expected to consume this
|
||||||
|
|
||||||
|
`SKILL.md` is deliberately written as a **procedure with gates** rather than as advice, because the
|
||||||
|
observed failure mode is not ignorance — it is a model reading the whole thing, agreeing with it, and
|
||||||
|
then reasoning from first principles anyway.
|
||||||
|
|
||||||
|
So there are four things in the body that are meant to be *acted on*, not read:
|
||||||
|
|
||||||
|
- **Four override rules (R1–R4).** Where they conflict with the current plan, they win until evidence
|
||||||
|
overrides them.
|
||||||
|
- **A symptom index.** Each row is a failure that has already been paid for. **A matching row is a
|
||||||
|
stop signal**: load that file before running another command, rather than after a few more attempts.
|
||||||
|
Reasoning past a known symptom is how the same hours get spent twice.
|
||||||
|
- **Four gates (G1–G4),** each an action with a pass criterion. "I understand the idea" does not clear
|
||||||
|
a gate. They exist so that classification, environment truth and a control build happen *before*
|
||||||
|
the first patch, not after the third failure.
|
||||||
|
- **A two-strike rule and stop conditions.** Two failures of the same shape mean the model is wrong,
|
||||||
|
not the parameters. The third variant of a hypothesis that already failed twice is where rounds go
|
||||||
|
to die.
|
||||||
|
|
||||||
|
And one thing at the end that is meant to be *withheld*: **"done"** has a definition (six items). A
|
||||||
|
clean log is not one of them. Anything short of all six is a checkpoint, and should be reported as a
|
||||||
|
checkpoint with what remains.
|
||||||
|
|
||||||
|
If you are an agent reading this: the cheapest possible first command is
|
||||||
|
`python scripts/doctor.py`. It tells you which of these tools exist here, which scripts can actually
|
||||||
|
run, and whether something in the environment is already poisoning your measurements.
|
||||||
|
|
||||||
## What it is good at
|
## What it is good at
|
||||||
|
|
||||||
- Deciding **fast** whether a request is even achievable client-side, instead of
|
- Deciding **fast** whether a request is even achievable client-side, instead of
|
||||||
@@ -49,7 +77,13 @@ directly.
|
|||||||
## Structure
|
## Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
SKILL.md decision tree, workflow, hard constraints, indexes
|
SKILL.md a procedure with gates, not background reading:
|
||||||
|
how-to-use -> four override rules (R1-R4)
|
||||||
|
symptom index (a matching row is a stop signal)
|
||||||
|
four gates (G1-G4, actions with pass criteria)
|
||||||
|
thirteen classification questions
|
||||||
|
the workflow, with a per-step skip condition and a two-strike rule
|
||||||
|
what "done" means -> stop conditions -> constraints -> indexes
|
||||||
references/ loaded on demand, one topic each
|
references/ loaded on demand, one topic each
|
||||||
recon.md identify packer, SDKs, code location, tamper checks; unpacking
|
recon.md identify packer, SDKs, code location, tamper checks; unpacking
|
||||||
packers.md hardened targets: rejection signals, measuring the validation
|
packers.md hardened targets: rejection signals, measuring the validation
|
||||||
|
|||||||
@@ -1,30 +1,68 @@
|
|||||||
---
|
---
|
||||||
name: apk-reverse
|
name: apk-reverse
|
||||||
description: Reverse engineer, debloat, de-ad, patch, or re-sign Android APKs, and analyze their runtime and server-side behavior. Use when a task involves an .apk/.aab/.dex/.so sample, smali or dex patching, Frida/objection runtime hooking, repacking and re-signing, removing ads or SDK trackers, probing a mobile app's HTTP API, or deciding whether a client-side patch is even capable of achieving the goal. Covers recon, anti-tamper, ad removal, membership/paywall limits, dex-level surgical patching, repack pitfalls, device and emulator setup, and a hard-won failure catalogue.
|
description: Reverse engineer, debloat, de-ad, patch, or re-sign Android APKs, and analyze their runtime and server-side behavior. Use when a task involves an .apk/.aab/.dex/.so sample, smali or dex patching, Frida/objection runtime hooking, repacking and re-signing, removing ads or SDK trackers, probing a mobile app's HTTP API, or deciding whether a client-side patch is even capable of achieving the goal. Covers recon, anti-tamper, ad removal, membership/paywall limits, dex-level surgical patching, repack pitfalls, device and emulator setup, and a hard-won failure catalogue. Load the body before planning any patch work: it opens with a symptom index and four gates that must be cleared first.
|
||||||
---
|
---
|
||||||
|
|
||||||
# APK Reverse Engineering & Patching
|
# APK Reverse Engineering & Patching
|
||||||
|
|
||||||
Goal of this skill: get to a **verified, installable, still-working artifact** fast, and avoid the whole class of mistakes that silently destroy an APK.
|
Goal: reach a **verified, installable, still-working artifact** fast — and avoid the whole class of
|
||||||
|
mistakes that destroy an APK while looking completely healthy.
|
||||||
|
|
||||||
Three rules override everything else in this skill:
|
## How to use this file
|
||||||
|
|
||||||
1. **Deliver it in the environment the request actually requires.** "It works" is not the goal; "it works under the stated constraint" is. A result obtained with root, or with live instrumentation, or with a host proxy, frequently does **not** satisfy a request for an installable artifact that works on a normal device — and it is easy to present that result as a finished one. Write the constraint down as a testable sentence in the first minutes and re-read it at every checkpoint. If you cannot meet it, say so and label any privileged workaround as a fallback, never as the deliverable. Full treatment: `references/long-task-discipline.md` §the most expensive drift.
|
This file is a **procedure with gates**, not background reading. Three things are mandatory:
|
||||||
2. **Never ship an unverified APK.** "It assembles" is not "it works". Install it, launch it, exercise the feature you changed, on a real device or a close emulator.
|
|
||||||
3. **Know which layer owns the behavior before you patch.** Ads, paywalls, and feature gates live in different places. Patching the wrong layer either does nothing or breaks the app. Classify first (see step 2 below), patch second.
|
|
||||||
|
|
||||||
## Symptom index — look here before you debug from first principles
|
- Before you patch anything, clear the **four gates** in §Gates. They are actions with pass criteria,
|
||||||
|
not attitudes.
|
||||||
|
- When anything fails in a way your current plan does not explain, **stop and check the symptom
|
||||||
|
index**. If a row matches, load that file before running another command.
|
||||||
|
- When this file and your own reasoning disagree, **this file wins** until you have evidence that
|
||||||
|
overrides it. Every rule here is the residue of a failure that cost hours; your current intuition is
|
||||||
|
the intuition of someone who has not hit it yet.
|
||||||
|
|
||||||
You arrived at a symptom, not at a file name. This table maps what you are *seeing* to the file that
|
## Four rules that override everything else
|
||||||
has already paid for that lesson. Skim it whenever something fails in a way the current plan does not
|
|
||||||
explain; the cost of one lookup is minutes, and every entry below cost someone hours.
|
**R1 — Write the deliverable as a testable sentence before you touch the target.**
|
||||||
|
"It works" is not the goal; "it works under the stated constraint" is. Root-assisted, live-
|
||||||
|
instrumentation, host-proxy and patched-device results frequently do **not** satisfy a request for an
|
||||||
|
installable artifact that works on a normal phone — and it is easy to present such a result as
|
||||||
|
finished. Write the sentence, re-read it at every checkpoint, and if you cannot meet it, say so
|
||||||
|
plainly and label the privileged workaround a **fallback**, never the deliverable.
|
||||||
|
→ `references/long-task-discipline.md` §the most expensive drift
|
||||||
|
|
||||||
|
**R2 — Change one variable at a time, and keep a control build.**
|
||||||
|
An experiment that flips two things teaches nothing when it fails, and a failure you cannot attribute
|
||||||
|
will be attributed to the wrong cause. Every "the app rejects X" claim needs its own run, and every
|
||||||
|
patch needs a same-pipeline control that still fails the old way.
|
||||||
|
→ `references/long-task-discipline.md` §single-variable discipline
|
||||||
|
|
||||||
|
**R3 — Never ship or claim an unverified artifact.**
|
||||||
|
"It assembles" is not "it works"; "the process started" is not "the feature works"; "no error in the
|
||||||
|
log" is not "the check is gone". Install it, launch it, exercise the exact feature you changed, and
|
||||||
|
look at the screen. Prove the device is running the build you made — hash it, do not trust the
|
||||||
|
filename.
|
||||||
|
→ `references/verification.md`
|
||||||
|
|
||||||
|
**R4 — Identify the owning layer before patching, and re-classify when reality disagrees.**
|
||||||
|
Ads, paywalls, feature gates, integrity checks and update gates live in different layers (Java, dex,
|
||||||
|
native, Dart/Unity, server). Patching the wrong layer either does nothing or breaks the app. If a
|
||||||
|
patch "had no effect", the diagnosis was wrong — go back to classification instead of patching harder.
|
||||||
|
→ `references/recon.md`, then the layer-specific file the symptom index points at
|
||||||
|
|
||||||
|
## Symptom index — a matching row is a stop signal
|
||||||
|
|
||||||
|
You arrive at a symptom, not at a file name. Each row below is a failure that has already been paid
|
||||||
|
for. **If any row matches what you are observing, load the file before your next command** — not after
|
||||||
|
your next three attempts. Reasoning from first principles at this point is how the same hours get
|
||||||
|
spent twice; more than one entry here is a lesson that was re-derived by hand while the answer sat
|
||||||
|
unread in this repository.
|
||||||
|
|
||||||
| What you observe | Load first |
|
| What you observe | Load first |
|
||||||
|---|---|
|
|---|---|
|
||||||
| A repackaged/re-signed build **dies before your code runs**; `SIGSEGV`, all registers zero, `pc=0`, `fault addr` near `0x0` | `native-tamper-and-suicide.md` (deliberate crash), then `code-virtualization-and-custom-linkers.md` |
|
| A repackaged/re-signed build **dies before your code runs**; `SIGSEGV`, all registers zero, `pc=0`, `fault addr` near `0x0` | `native-tamper-and-suicide.md` (deliberate crash), then `code-virtualization-and-custom-linkers.md` |
|
||||||
| **No packer** (Application is the app's own, dex readable) **and it still dies** | `code-virtualization-and-custom-linkers.md` |
|
| **No packer** (Application is the app's own, dex readable) **and it still dies** | `code-virtualization-and-custom-linkers.md` |
|
||||||
| Log says a **Java-layer** signature/integrity check **passed**, yet the process dies | `code-virtualization-and-custom-linkers.md` §a Java-layer signature killer is a decoy |
|
| Log says a **Java-layer** signature/integrity check **passed**, yet the process dies | `code-virtualization-and-custom-linkers.md` §a Java-layer "signature killer" is a decoy |
|
||||||
| Deleting a library fixes validation but yields `UnsatisfiedLinkError: dlopen failed: library "X" not found` | `code-virtualization-and-custom-linkers.md` §the deadlock |
|
| Deleting a library fixes validation but yields `UnsatisfiedLinkError: dlopen failed: library "X" not found` | `code-virtualization-and-custom-linkers.md` §the deadlock that eats hours |
|
||||||
| Whole classes appear as bare `native` declarations with no body | `code-virtualization-and-custom-linkers.md` |
|
| Whole classes appear as bare `native` declarations with no body | `code-virtualization-and-custom-linkers.md` |
|
||||||
| A library's **SONAME does not match its filename** | `code-virtualization-and-custom-linkers.md`, `native-and-so.md` |
|
| A library's **SONAME does not match its filename** | `code-virtualization-and-custom-linkers.md`, `native-and-so.md` |
|
||||||
| Your edit had **no effect at all**, with no error | `packers.md` §map the validation boundary |
|
| Your edit had **no effect at all**, with no error | `packers.md` §map the validation boundary |
|
||||||
@@ -42,6 +80,34 @@ explain; the cost of one lookup is minutes, and every entry below cost someone h
|
|||||||
| You are about to discard a route as "blocked" | `packers.md` — re-read it before writing any route off; mis-attributed failures have removed viable routes for hours |
|
| You are about to discard a route as "blocked" | `packers.md` — re-read it before writing any route off; mis-attributed failures have removed viable routes for hours |
|
||||||
| The task has run long and you are unsure what is already proven | `long-task-discipline.md` §keep a live record |
|
| The task has run long and you are unsure what is already proven | `long-task-discipline.md` §keep a live record |
|
||||||
|
|
||||||
|
## Gates — clear these before you patch, in order
|
||||||
|
|
||||||
|
Each gate is an **action with a pass criterion**. Do not proceed past a gate you have not cleared, and
|
||||||
|
do not treat "I understand the idea" as clearing it. Skipping a gate is not a shortcut; it is how the
|
||||||
|
work gets redone.
|
||||||
|
|
||||||
|
**G1 · Deliverable form.** State, in one sentence you could hand to someone else, what artifact must
|
||||||
|
exist at the end and under what constraints (rooted or not, installable on a stock device or not, must
|
||||||
|
survive updates or not, online or offline). *Pass:* the sentence names a testable constraint, not an
|
||||||
|
activity. *Fail:* you are solving a problem in an environment the deliverable will never see.
|
||||||
|
|
||||||
|
**G2 · Environment truth.** Run `scripts/doctor.py` (and `scripts/preflight.py` if a device is in
|
||||||
|
play). *Pass:* you know which toolchains and scripts can actually run here, and you have seen the
|
||||||
|
environment warnings — clock skew, leftover `adb forward`/proxy, a device-side frida process already
|
||||||
|
running, a tool installed off-PATH. *Fail:* you are about to attribute to the target a failure caused
|
||||||
|
by your own setup.
|
||||||
|
|
||||||
|
**G3 · Code location.** From the manifest and dex, answer: is there a packer, where does the app's own
|
||||||
|
code live (dex / native / Dart / Unity / server), and is any of it virtualized to native. *Pass:* you
|
||||||
|
can name the class that owns the behaviour you intend to change, or you have an explicit plan to find
|
||||||
|
it. *Fail:* you are about to patch a layer you have not located. If recon says "no packer", still
|
||||||
|
check the virtualization shape — see the index rows above.
|
||||||
|
|
||||||
|
**G4 · Baseline and control.** *Pass:* you have a control run — the unmodified original, or a
|
||||||
|
zero-change repack through the same pipeline — and you have recorded the observed failure (including
|
||||||
|
**time-to-death**, if it dies). *Fail:* when the patched build misbehaves you will have nothing to
|
||||||
|
compare against, and every later measurement is unfalsifiable.
|
||||||
|
|
||||||
|
|
||||||
## Start here: classify the target in thirteen questions
|
## Start here: classify the target in thirteen questions
|
||||||
|
|
||||||
@@ -110,9 +176,19 @@ Answer these before touching a tool. Every one of them changes the whole plan.
|
|||||||
|
|
||||||
## The workflow, end to end
|
## The workflow, end to end
|
||||||
|
|
||||||
|
Steps are ordered. **Skip a step only when its stated skip condition is met** — "it seems
|
||||||
|
unnecessary" is not a condition, and it is the reason most of the failures in `pitfalls.md` happened.
|
||||||
|
|
||||||
|
**Two-strike rule.** If the *same kind* of attempt fails twice, stop and go back to classification.
|
||||||
|
Do not run a third variation of a hypothesis that has already failed twice. Two failures of one shape
|
||||||
|
means the model is wrong, not that the parameters need tuning — and the third attempt is where an
|
||||||
|
entire round gets spent confirming what the first two already said. Re-read the symptom index at that
|
||||||
|
point; it exists for exactly this moment.
|
||||||
|
|
||||||
1. **Preflight, then Recon** — `scripts/doctor.py` is the cheapest possible first command: it reports which toolchains and scripts can actually run here, and surfaces the environment facts that poison experiments (clock skew, leftover `adb forward`/proxy, a device-side frida process already running, a tool installed off-PATH). Then `scripts/preflight.py` before anything else if a device is involved (it takes seconds and prevents a whole class of false conclusions), then `references/recon.md`. Manifest, package name, version, ABI, dex count, packer, embedded SDKs, where the app's own code lives. Ten minutes here saves hours. **If it is packed, unpack before anything else** (`references/recon.md` §unpacking): you cannot patch code you cannot read, the encrypted payload lengths tell you which dumped dex is the original, and a memory dump must be de-duplicated by hash and structurally validated before any of it is trusted.
|
1. **Preflight, then Recon** — `scripts/doctor.py` is the cheapest possible first command: it reports which toolchains and scripts can actually run here, and surfaces the environment facts that poison experiments (clock skew, leftover `adb forward`/proxy, a device-side frida process already running, a tool installed off-PATH). Then `scripts/preflight.py` before anything else if a device is involved (it takes seconds and prevents a whole class of false conclusions), then `references/recon.md`. Manifest, package name, version, ABI, dex count, packer, embedded SDKs, where the app's own code lives. Ten minutes here saves hours. **If it is packed, unpack before anything else** (`references/recon.md` §unpacking): you cannot patch code you cannot read, the encrypted payload lengths tell you which dumped dex is the original, and a memory dump must be de-duplicated by hash and structurally validated before any of it is trusted.
|
||||||
**If recon says there is no packer but a re-signed build still dies**, you are in the layer `references/code-virtualization-and-custom-linkers.md` covers — do not proceed on the assumption that "no packer" means "editable".
|
**If recon says there is no packer but a re-signed build still dies**, you are in the layer `references/code-virtualization-and-custom-linkers.md` covers — do not proceed on the assumption that "no packer" means "editable".
|
||||||
**If the app already dies on its own** — especially at a roughly constant time after launch, or with a native crash — locate the mechanism *before* planning any patch (`references/native-tamper-and-suicide.md`, `scripts/native_crash.py`). Record the observed time-to-death: it is the baseline every later attempt is measured against, and without it a surviving run cannot be told from a changed schedule.
|
**If the app already dies on its own** — especially at a roughly constant time after launch, or with a native crash — locate the mechanism *before* planning any patch (`references/native-tamper-and-suicide.md`, `scripts/native_crash.py`). Record the observed time-to-death: it is the baseline every later attempt is measured against, and without it a surviving run cannot be told from a changed schedule.
|
||||||
|
*Skip condition:* never skipped. G2/G3 in §Gates are cleared here or not at all.
|
||||||
2. **Extract strings and endpoints** — build a picture of the app's API surface and SDK inventory from the dex string tables. No decompiler needed for this, and it is fast. Scripts: `scripts/dex_strings.py`.
|
2. **Extract strings and endpoints** — build a picture of the app's API surface and SDK inventory from the dex string tables. No decompiler needed for this, and it is fast. Scripts: `scripts/dex_strings.py`.
|
||||||
3. **Trace to the owning class** — find the class that wraps the behavior (the app almost always wraps third-party SDKs in one helper). Reverse-lookup instructions: `references/dex-patching.md` §finding-the-call-site.
|
3. **Trace to the owning class** — find the class that wraps the behavior (the app almost always wraps third-party SDKs in one helper). Reverse-lookup instructions: `references/dex-patching.md` §finding-the-call-site.
|
||||||
4. **Decide the patch layer** — client SDK call / client rendering / client data consumption / server contract. See the table in `references/ad-removal.md`.
|
4. **Decide the patch layer** — client SDK call / client rendering / client data consumption / server contract. See the table in `references/ad-removal.md`.
|
||||||
@@ -123,6 +199,41 @@ Answer these before touching a tool. Every one of them changes the whole plan.
|
|||||||
7. **Verify on device** — `references/environment.md` + `references/verification.md`. Check: launches, the changed behavior actually changed, nothing unrelated broke, and **the app reaches its normal UI with no blocking dialog**. First prove the artifact actually changed on the device -- a package manager reporting success does not prove an interposed confirmation was accepted (P18). Capture continuously for the first ~20 seconds after launch, **and look at the captures** — sampling gaps are how a blocking modal goes unseen (P20), and a burst of images that were never inspected is not evidence. If the accessibility tree is empty, the image is the primary evidence rather than a fallback.
|
7. **Verify on device** — `references/environment.md` + `references/verification.md`. Check: launches, the changed behavior actually changed, nothing unrelated broke, and **the app reaches its normal UI with no blocking dialog**. First prove the artifact actually changed on the device -- a package manager reporting success does not prove an interposed confirmation was accepted (P18). Capture continuously for the first ~20 seconds after launch, **and look at the captures** — sampling gaps are how a blocking modal goes unseen (P20), and a burst of images that were never inspected is not evidence. If the accessibility tree is empty, the image is the primary evidence rather than a fallback.
|
||||||
8. **Log what you learned** — if a failure cost you more than thirty minutes, add it to `references/pitfalls.md`. That file is the most valuable artifact in this skill.
|
8. **Log what you learned** — if a failure cost you more than thirty minutes, add it to `references/pitfalls.md`. That file is the most valuable artifact in this skill.
|
||||||
|
|
||||||
|
## What "done" means — do not claim it earlier
|
||||||
|
|
||||||
|
Every item below must be true before you report completion. Anything less is a **checkpoint** and must
|
||||||
|
be labelled as one, out loud, with what remains. Premature "done" is the most damaging thing you can
|
||||||
|
report, because it ends the investigation while the user believes the problem is solved.
|
||||||
|
|
||||||
|
1. **The artifact exists and its identity is recorded** — path plus hash, not a filename.
|
||||||
|
2. **It was installed and launched on the environment the deliverable sentence names** (G1/R1). If
|
||||||
|
that environment was not available to you, say so and label the result accordingly.
|
||||||
|
3. **The behaviour you changed is verified changed** — by direct observation of the feature, not by
|
||||||
|
the absence of an error message. "The log is clean" is not evidence; "the screen shows X" is.
|
||||||
|
4. **The features it touches still work.** You exercised them. A build that starts but whose affected
|
||||||
|
feature is dead is not a result.
|
||||||
|
5. **The original limitation is stated if any survives** — with the coupling that causes it, so the
|
||||||
|
next person can decide whether to accept it.
|
||||||
|
6. **Nothing you did leaves the target or the device in a broken state** unless that was the goal, and
|
||||||
|
any privileged workaround is labelled a fallback rather than the deliverable.
|
||||||
|
|
||||||
|
If items 1–4 hold but the environment was wrong, you have a **prototype**, not a deliverable. Say
|
||||||
|
"prototype" and name the gap.
|
||||||
|
|
||||||
|
## Stop conditions — halt and re-classify, do not retry
|
||||||
|
|
||||||
|
These are moments where continuing to push forward is the wrong move. Each has cost hours somewhere.
|
||||||
|
|
||||||
|
- **The same shape of attempt failed twice.** See the two-strike rule above.
|
||||||
|
- **A patch had no effect and you were about to try a third variant of it.** No effect means the
|
||||||
|
diagnosis was wrong, not that the patch was unlucky. Re-classify the layer.
|
||||||
|
- **A new failure has no place in your current model.** That is the symptom index's trigger condition.
|
||||||
|
- **You are about to write off a route as "blocked"** without a control build proving the block is
|
||||||
|
the app's doing rather than your pipeline's. Mis-attributed blocks have removed viable routes.
|
||||||
|
- **You are about to claim success on absence of errors.** See §What "done" means.
|
||||||
|
- **A measurement disagrees with a conclusion you already recorded as settled.** Re-open the
|
||||||
|
conclusion; do not explain the measurement away.
|
||||||
|
|
||||||
## Non-negotiable constraints
|
## Non-negotiable constraints
|
||||||
|
|
||||||
- **Read-only inputs.** Keep the original APK/dex untouched; work on copies. Always keep a known-good baseline to diff against.
|
- **Read-only inputs.** Keep the original APK/dex untouched; work on copies. Always keep a known-good baseline to diff against.
|
||||||
|
|||||||
Reference in New Issue
Block a user