refactor(memory-safety): add a parse wrapper, scan mobile/app, pin the lint wiring

- parseJsonWithinStructureLimits guards then parses in one call, so a bare
  JSON.parse genuinely means unguarded instead of every sanctioned call site
  needing a justification comment.
- scan mobile/app (Expo Router screens) alongside mobile/src.
- test that package.json still wires check:bounded-ingress into lint: a gate
  that can be unwired without a test failing will be unwired by a rebase.

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Neil
2026-07-25 23:43:51 -07:00
co-authored by Orca
parent 00d7e3fa6a
commit d192c52135
4 changed files with 27 additions and 2 deletions
+2
View File
@@ -1,5 +1,7 @@
{
"allowed": {
"mobile/app/h/[hostId]/session/[worktreeId].tsx:json-stringify": 1,
"mobile/app/index.tsx:json-parse": 1,
"mobile/src/cache/home-snapshot-cache.ts:json-parse": 1,
"mobile/src/cache/home-snapshot-cache.ts:json-stringify": 1,
"mobile/src/components/CustomKeyModal.tsx:json-parse": 1,
+3 -2
View File
@@ -35,7 +35,7 @@ export const PATTERNS = [
{
id: 'json-parse',
re: /\bJSON\.parse\s*\(/,
hint: 'guard with assertJsonTextStructureWithinLimits (memory-safety/json-text-structure-limit)'
hint: 'use parseJsonWithinStructureLimits (memory-safety/json-text-structure-limit)'
},
{
id: 'json-stringify',
@@ -128,7 +128,8 @@ async function collect(root, dir, out = []) {
export async function run(root, { updateBaseline = false } = {}) {
const files = [
...(await collect(root, path.join(root, 'src'))),
...(await collect(root, path.join(root, 'mobile/src')))
...(await collect(root, path.join(root, 'mobile/src'))),
...(await collect(root, path.join(root, 'mobile/app')))
]
const violations = []
for (const file of files) {
@@ -77,6 +77,17 @@ describe('check-bounded-ingress', () => {
).toEqual([])
})
it('stays wired into pnpm lint', () => {
// A gate that can be unwired without any test failing WILL be unwired by a rebase — this one
// already was once, and the ratchet cannot detect its own removal.
const root = path.resolve(import.meta.dirname, '../..')
const pkg = JSON.parse(readFileSync(path.join(root, 'package.json'), 'utf8'))
expect(pkg.scripts['check:bounded-ingress']).toBe(
'node config/scripts/check-bounded-ingress.mjs'
)
expect(pkg.scripts.lint).toContain('check:bounded-ingress')
})
it('every hint names a real export of the module it points at', () => {
// The hint is the text an agent copies when blocked; a stale name is an immediate import error.
const root = path.resolve(import.meta.dirname, '../..')
@@ -78,3 +78,14 @@ function isStructuralToken(character: string | undefined): boolean {
character === ':'
)
}
// Guard-then-parse in one call so a bare `JSON.parse` in review (and in the bounded-ingress ratchet)
// genuinely means "unguarded", instead of every sanctioned call site needing a justification comment.
export function parseJsonWithinStructureLimits<T>(
content: string,
limits: JsonTextStructureLimits
): T {
assertJsonTextStructureWithinLimits(content, limits)
// bounded-by: structure asserted on the line above
return JSON.parse(content) as T
}