Memory-driven lint — #1901

Lessons in CC-native memory (feedback_*.md) become runtime tool-invocation defenses — no PR per lesson. The crux is a safe predicate DSL: rules come from memory files, so there is no eval anywhere.
new lib memory-lint.ts
tests 27
hooks suite 8579/0
eval calls 0

The bridge

feedback_*.md
```ork-lint [...]```
getMemoryRules()
parse · validate · compile
tool-invocation-linter
(PreToolUse)
⚠️ advisory [memory:<file>]

Static TS registry (#1883) wins on id collision; memory rules are advisory-only.

Declare a rule in memory

```ork-lint
[
  { "id": "no-no-verify", "tool_name": "Bash",
    "predicate": { "field": "command", "op": "contains", "value": "--no-verify" },
    "severity": "warn", "message": "do not bypass hooks", "see": "CLAUDE.md" }
]
```

Fenced JSON (not YAML) — hooks are zero-dep, so JSON.parse beats a hand-rolled YAML parser.

Safe predicate DSL — data, never code

opmatches when field…
eq / neqequals / (present and ≠) value
inequals one of value[]
prefix / containsstarts-with / contains value
exists / absentis present / missing

🔒 Security model (rules come from memory files)

No evalcompilePredicate is a pure switch; value:"process.exit(1)" is a literal string compared with ===, never executed.
No blocking — memory rules are warn/info only; severity:"block" is rejected at load. A memory file can never halt a tool call.
Prototype-pollution guarded field resolution (__proto__/constructor never resolve).
Fail-soft — invalid entries skipped with a collected error; a malformed memory file never crashes the linter.

Tested

27 unit/integration cases: DSL eval · prototype guard · no-eval injection ·
   block-severity rejection · malformed-JSON degrade · dir resolution ·
   full linter integration ([memory:<file>] advisory fires)
opt-out: ORK_NO_MEMORY_LINT=1   ·   promotion path → static registry (#1883)