danielmiessler / danielmiessler/LifeOS

Interceptor Reproduce.md step 3 "Check Console Errors" emits consoleCheck: done from a probe that never reads the console

Open
#2,088 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
19k
Forks
2.5k
Avg merge
8d 17h
Merged PRs (30d)
1

Description

## Version

LifeOS 7.40.4 / Interceptor

## What is broken

`Workflows/Reproduce.md` step 3 is titled **"Check Console Errors"** and step 5 asks the operator to
document *"Console errors found (with specific error messages)"*. The probe those steps rely on
never reads the console. It calls `performance.getEntriesByType('resource')` — the Resource Timing
API — filters for `.js`/`.css` entries with zero transfer and zero decoded size, and returns
`{ consoleCheck: 'done', failedResources: [...] }`. `consoleCheck: 'done'` is a string literal in
the return object: it is emitted on every run, whatever the page did, and it is the only thing in
the output that refers to the console.

So a page with an uncaught `TypeError` yields `consoleCheck: 'done'` and an empty list, and the
workflow's own next step asks the operator to write down the console errors this step just told
them were checked.

To be fair to the probe: it is not useless, and it covers part of what the workflow was written
for. `Reproduce.md:94` names the incidents that justify the workflow as *"missing JS chunks, 404s
on bundles, CORS errors"*, and a resource-timing scan for zero-transfer `.js`/`.css` entries is a
reasonable instrument for the first two of those three. The gap is the rest of the console:
runtime exceptions, unhandled promise rejections, and the CORS errors named in that same sentence,
none of which produce a failed resource entry and all of which surface only in the console. Those
are what the step's title, its consumer at `:86`, and *"visible in the browser console in under a
minute"* are about, and they are the ones a run following this workflow will miss while being told
the console was checked.

Title, probe and consumer disagree:

- `LifeOS/install/skills/Interceptor/Workflows/Reproduce.md:52` — the step title, "Check Console Errors"
- `LifeOS/install/skills/Interceptor/Workflows/Reproduce.md:55-58` — the probe body (`:54` is the fence line): `return JSON.stringify({ consoleCheck: 'done', failedResources: failed.map(e => e.name) });`
- `LifeOS/install/skills/Interceptor/Workflows/Reproduce.md:86` — the consumer, step 5: "Console errors found (with specific error messages)"
- `LifeOS/install/skills/Interceptor/Workflows/Reproduce.md:94` — the stated reason for the workflow: "missing JS chunks, 404s on bundles, CORS errors" — "visible in the browser console in under a minute"

## Where (file:line)

`LifeOS/install/skills/Interceptor/Workflows/Reproduce.md:58`

## Repro on a clean tree

```shell
git clone --branch v7.40.4 --depth 1 https://github.com/danielmiessler/LifeOS.git /tmp/lifeos-7404
cd /tmp/lifeos-7404 && git rev-parse HEAD
# → be9e8ef889f00a29f4fd677dee4772fdf32e07ce

sed -n '52,58p' LifeOS/install/skills/Interceptor/Workflows/Reproduce.md

# Run the probe body verbatim against a page that HAS a console error
# and NO failed resource, so the only thing worth reporting is the error.
cat > /tmp/q3.ts <<'EOF'
const consoleErrors: string[] = [
"Uncaught TypeError: Cannot read properties of undefined (reading 'mount')",
];
(globalThis as any).performance = {
getEntriesByType: (t: string) => t === 'resource' ? [
{ name: 'https://app.example.com/main.js', transferSize: 12043, decodedBodySize: 40122 },
{ name: 'https://app.example.com/main.css', transferSize: 2210, decodedBodySize: 8801 },
] : [],
};
// --- Reproduce.md:55-58, byte-for-byte ---
const probeResult = (() => {
const entries = performance.getEntriesByType('resource').filter(e => e.name.includes('.js') || e.name.includes('.css'));
const failed = entries.filter(e => e.transferSize === 0 && e.decodedBodySize === 0);
return JSON.stringify({ consoleCheck: 'done', failedResources: failed.map(e => e.name) });
})();
// ---
console.log('page console errors present :', consoleErrors.length);
console.log('probe returned :', probeResult);
console.log('probe mentions the error? :', probeResult.includes('TypeError') ? 'YES' : 'NO');
EOF
bun /tmp/q3.ts
# → page console errors present : 1
# → probe returned : {"consoleCheck":"done","failedResources":[]}
# → probe mentions the error? : NO

# The step reports `done` and nothing else. The operator, following step 5, has
# nothing to write down and no indication anything was missed.
```

## Negative control

Same page state, same runtime, a probe that reads the error surface instead of the resource
timeline. If the error were unreachable from inside the page this would also come back empty, and
the finding would be about the harness rather than the probe:

```shell
cat > /tmp/q3-control.ts <<'EOF'
const captured: string[] = [];
const orig = console.error;
console.error = (...a: any[]) => { captured.push(a.join(' ')); };
console.error("Uncaught TypeError: Cannot read properties of undefined (reading 'mount')");
console.error = orig;
const out = JSON.stringify({ consoleErrors: captured });
console.log('probe returned :', out);
console.log('probe mentions the error? :', out.includes('TypeError') ? 'YES' : 'NO');
process.exit(out.includes('TypeError') ? 1 : 0);
EOF
bun /tmp/q3-control.ts; echo "EXIT=$?"
```

```
probe returned : {"consoleErrors":["Uncaught TypeError: Cannot read properties of undefined (reading 'mount')"]}
probe mentions the error? : YES
EXIT=1
```

Red: the error is observable from inside the page on the same run. What differs between the two is
the surface the probe reads.

## Suggested fix

Shape only, **untested**: either capture the console (a `console.error`/`onerror`/
`unhandledrejection` collector installed before navigation, read back after) and report what it
holds, or retitle the step to what it actually measures — a failed-resource check — and drop the
`consoleCheck` key, since a constant `'done'` carries no information either way. The second is
smaller and removes the false claim; the first is what the step's title, its consumer at line 86,
and the workflow's stated reason for existing all ask for.

One adjacent observation, **not verified in a browser** and offered only so it is not lost: the
`transferSize === 0 && decodedBodySize === 0` predicate is also the documented shape of a
cross-origin resource served without `Timing-Allow-Origin`, so a CDN bundle that loaded fine may be
reported as failed. I did not test this and it should not be treated as established.

## Before submitting

- [x] I searched open and closed issues for this defect.
Searched `consoleCheck`, `Reproduce workflow console`, `Interceptor preflight isolation`,
`Capture.sh`. No prior report. The Interceptor cluster (#1802, #1892, #2010, #2011, #2013,
#2065, #1775) covers other tools and other defects; none touches `Reproduce.md` step 3.
- [x] The repro runs against a clean tree of the version above, not against my modified install.
Fresh `--depth 1` clone of `v7.40.4`; the probe body is copied byte-for-byte from the clone.
- [x] I removed personal data from the pasted output — real names, absolute home paths, tokens, my
own content. Paths are `/tmp/...` and repo-relative; the page and error are synthetic.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with LifeOS/install/skills/Interceptor/Workflows/Reproduce.md:52-58 and read how step 5 at :86 consumes the probe output. Run the clean-tree reproduction with bun to confirm that a page console error is omitted, then compare the chosen fix against the workflow rationale at :94. Done means the step and its output accurately report the intended error surface without contradicting the consumer.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.