Commit Graph
536 Commits
Author SHA1 Message Date
Marius Ciepluch 4a1167dfdd feat(detection-core): 8.1 prover native conformance to core trace/coverage contracts 2026-10-30 18:37:04 +01:00
Marius Ciepluch af400c6be4 feat(detection-core): runner migration onto core contracts, schema validation, E2E 2026-10-30 17:12:33 +01:00
Marius Ciepluch c924a69514 feat(detection-core): fact-pack admission and push-consumer contracts 2026-10-30 17:05:24 +01:00
Marius Ciepluch 43fd59f960 feat(detection-core): anti-shortcut probes and fitted-component admission gate 2026-10-30 16:27:27 +01:00
Marius Ciepluch 46b4579d01 feat(detection-core): generalized admission contract with family-scope reports 2026-10-30 16:22:40 +01:00
Marius Ciepluch eb9fe856f9 feat(detection-core): prover trace/coverage conformance adapters to the core contract 2026-10-30 16:18:22 +01:00
Marius Ciepluch 92fb372498 feat(detection-core): measured per-class budget replacing the static 120s gate 2026-10-30 16:11:37 +01:00
Marius Ciepluch 0b56ad5514 feat(detection-core): foundation contract package (trace, verdict, coverage, provenance) 2026-10-30 16:04:10 +01:00
Marius Ciepluch 85aa3b1484 feat(access-control-prover): 11.7 first label-blind development acquisition 2026-10-29 21:04:51 +01:00
Marius Ciepluch acadf04109 feat(access-control-prover): 11.6 migrate the stapler recogniser through the registry 2026-10-29 20:59:37 +01:00
Marius Ciepluch 3f241bc595 feat(access-control-prover): 11.5 acquisition provenance and label-isolated mode 2026-10-29 20:49:39 +01:00
Marius Ciepluch 09c3ffd621 feat(access-control-prover): 11.4 verify and promote proposed target knowledge 2026-10-29 20:34:52 +01:00
Marius Ciepluch c0e1360149 feat(access-control-prover): 11.3 extract existing target knowledge into candidate records 2026-10-29 20:29:15 +01:00
Marius Ciepluch c279513def feat(access-control-prover): 11.2 generic call-site registration strategy 2026-10-29 20:23:50 +01:00
Marius Ciepluch 86709daf2e feat(access-control-prover): 11.1 entry-recognition strategy registry 2026-10-29 20:19:31 +01:00
Marius Ciepluch 82e4a1ad97 feat(access-control-prover): 10.6 three-way judgments in the prover 2026-10-29 19:34:44 +01:00
Marius Ciepluch 95dfddb018 feat(access-control-prover): 10.5 multi-path aggregation in the opt-in abstract mode 2026-10-29 19:20:24 +01:00
Marius Ciepluch f700b99637 feat(access-control-prover): 10.4 one judgment per path 2026-10-29 19:17:37 +01:00
Marius Ciepluch 7eb491dc82 feat(access-control-prover): 10.3 authority matching through promoted implications 2026-10-29 19:11:30 +01:00
Marius Ciepluch cf60e02804 feat(access-control-prover): 10.2 witness kind, scope and resource matching 2026-10-29 19:05:51 +01:00
Marius Ciepluch 8ad304ffeb feat(access-control-prover): 10.1 abstract dominance relations without prover fallback 2026-10-29 19:01:25 +01:00
Marius Ciepluch f23d3f986d feat(access-control-prover): 9.4 exact evidence paths from summaries 2026-10-29 18:46:41 +01:00
Marius Ciepluch 06d5363ce2 feat(access-control-prover): 9.3 summary caches and explicit exploration cutoffs 2026-10-29 18:37:06 +01:00
Marius Ciepluch c237626f7a feat(access-control-prover): 9.2 monotone summaries over recursive call components 2026-10-29 18:30:18 +01:00
Marius Ciepluch fffa3e0002 feat(access-control-prover): 9.1 local function summaries 2026-10-29 18:25:23 +01:00
Marius Ciepluch 2466670ba3 feat(access-control-prover): 8.4 inheritance and base-declaration attachment through repo-defined descendants 2026-10-29 16:10:44 +01:00
Marius Ciepluch 8ef2629e0b feat(access-control-prover): 8.3 exact, finite-candidate and unresolved call targets 2026-10-29 16:04:51 +01:00
Marius Ciepluch 6f2154fae0 feat(access-control-prover): 8.2 stable declaration identities and per-change symbol index 2026-10-29 15:45:29 +01:00
Marius Ciepluch 9eee4d7f35 feat(access-control-prover): 8.1 capture call receiver and arity evidence in the semantic IR 2026-10-29 15:33:18 +01:00
Marius Ciepluch 03807f4ddd feat(access-control-prover): task 7.4 - judgment, trace and coverage contracts with abstract state 2026-10-29 15:23:43 +01:00
Marius Ciepluch 2f97fb2693 feat(access-control-prover): 7.3 authority abstract values and promoted implication facts 2026-10-29 15:06:53 +01:00
Marius Ciepluch 351583a04f feat(access-control-prover): 7.2 resource abstract values and operation-fact fields 2026-10-29 14:48:53 +01:00
Marius Ciepluch 5697a7fe9e feat(access-control-prover): 7.1 evidence-relation algebra and completeness value 2026-10-29 14:11:13 +01:00
Marius CiepluchandClaude Fable 5.1 03b114cbeb feat(prover): delivery D: control flow decides dominance; the trace carries each guard's relation, effect and the operation's control context
Opus agent delivery under spec access-control-prover, requirement 10. `semantic/cfg.py` builds a control-flow
graph per function from the syntax tree (if/else, switch arms, loops, try/catch/finally, return, throw, break,
continue, exit, assert) with dominators and a per-line control context, and leaves a function without a graph
rather than a wrong one. `obligations/cfg_dominance.py::CfgDominance` discharges a statement guard only when it
lies on every path to the operation and the operation is unreachable from its failing arm; a predicate whose
result nothing consumes is `value_only` and never discharges; router, decorator and earlier-hop witnesses behave
as before; without a graph the order rule applies and `cfg_unavailable` is recorded. The trace's guards carry
`relation` and `effect`, the trace carries `operation_context`; Java parameter lists carry names only; a
handler-named operation's arguments are the handler's parameters. The checker's default stays order-based.

Smoke on the 23 fix changes: 15 caught, 55 of 93 locations, precision 15 of 15, no failure, no change over
120 s (unchanged from delivery B). Gate: 3575 tests pass; the reserved-repository guard passes on the export.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-14 19:01:07 +02:00
Marius CiepluchandClaude Fable 5.1 2f628b921b feat(prover): delivery C: the ordered trace with argument provenance for every examined pair
Codex delivery under spec access-control-prover, requirement 9. `semantic/obligations/trace.py` serialises each
(entry, operation) pair the prover examined, discharged or unguarded: the entry with its boundary, access and
parameters; each hop in order with its call line, the guards before the call and the values forwarded; the
operation with the provenance of each argument traced through the function's binds; the guards after it; the
verdict. One canonical text form and one JSON form, no language, framework, repository, size or count fields.
The prover result, the arm record and each unguarded finding carry the traces. On the ec2-plugin fix: 32
traces, 4 unguarded. Gate: 3534 tests pass; the reserved-repository guard passes on the tracked export.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-14 18:22:45 +02:00
Marius CiepluchandClaude Fable 5.1 17e07b36c4 feat(prover): delivery B: handler-named operations, Symfony, Livewire, Remix and Tornado entries, enforcement witnesses, memoised path walk
Codex delivery under spec access-control-prover plus one fix of mine. Operation rows may now name a handler by
name pattern and framework (Jenkins form validation and list filling, Tornado request methods, Remix actions);
the mapper recognises Symfony, Livewire and Laravel controllers, Remix exports and Tornado handlers;
`obligations/enforcement.py` turns Laravel route middleware, Symfony attributes and security.yaml rules and
Tornado's authenticated decorator into router and decorator witnesses. The path walk looks each node's callers
up once per change and does not walk a tree with no recognised entry: one seatunnel change fell from 670 s to
35 s. One name Codex could not source (Tornado get_current_user) is left out and said so in the facts header.

Smoke on the 23 fix changes: 15 pushes caught (65 percent, Wilson 45 to 81), 55 of 93 locations, precision
15 of 15 flagged, no instrument failure, no change over 120 s. Delivery A caught 7.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-14 17:31:07 +02:00
Marius CiepluchandClaude Fable 5.1 beb272a5ec feat(prover): access-control prover, delivery A: path records, Stapler and Jetty entries, Java operation rows, the prover and its scoring arm
Codex delivery under spec access-control-prover. `semantic/obligations/paths.py` builds entry-to-function path
records from the cached caller index; `mapping.py` names Stapler web methods and Jetty handlers as entries;
`check_obligations` gains the opt-in premise `assume_entries_protected` with the label `unguarded_path`;
`hunt/prover.py` proves a change with no model call and states its coverage; `benchmarks/valid/prover_arm.py`
scores it with the valid-benchmark metrics. Two fixes after the first smoke run: a product file git holds empty
is read, not unread (four changes failed on `__init__.py` and empty assets), and absent-input reasons are
counted by name.

Smoke on the 23 fix changes of the training pool: 7 pushes caught (30 percent, Wilson 16 to 51), 28 of 93
locations, precision 7 of 8 flagged, no instrument failure; three changes over the 120 second bound. Twelve of
the 17 Jenkins fixes reach the fixed method but check no operation because the method is the operation;
the spec now carries handler-named operations for delivery B.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-14 16:50:04 +02:00
Marius CiepluchandClaude Fable 5.1 9feb8f1e33 fix(hunt): a row's entry points are the ones in its file or a caller's file, not the whole repository's list
The DSPy arm over the second pool failed with LMInvalidRequestError on every change with more than two rows, at no
cost: `source.facts_for` attached every entry point of the product tree to every row, 8190 records and 1.5 MB per
row on apache/seatunnel (the first pool's arm lost 11 changes the same way on trigger.dev and shopper), and the
rendered prompt was refused. The screen table of the second pool weighed 814 MB for the same reason.

A row now carries the entry points declared in its own file or in a file that calls the function, which is what the
input has described all along ("in-repository entry points that reach the file"). `source.entry_point_count`
follows that definition.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-14 14:47:08 +02:00
Marius CiepluchandClaude Fable 5.1 81ae206978 fix(hunt): build output gets no candidate rows, a minified line is not scanned once per function, the screen checkpoints every change
The second access-control screen spun for 2h20m on one ordinary mage-ai change: a Next.js build commit whose 3 MB
one-line bundles hold 12,355 functions each. `source.ranges_from_ir` ran two regex scans of the whole line once per
function, and the screen kept its 422 finished changes in memory only, so stopping it lost them.

- `source.ranges_from_ir`: a line over MINIFIED_LINE_CHARS (2000) is not checked per function; JavaScript names on it
  stay anonymous, other languages keep the parser's name.
- `source.generated_reason`: why a changed file is build output (a directory the product walk leaves out, a minified
  name, over MAX_FILE_BYTES, a line over the cap). `hunt.producers._candidates` leaves such files out and records the
  reason on the context; the fix site of a vulnerability is always kept. The DSPy arm shares the enumeration.
- The screen writes rows.jsonl and log.jsonl per change (rows first, entry last) and resumes by skipping logged ids.

Tests: the minified line never reaches the per-function scan; generated_reason on the four shapes and two source files;
build output in an ordinary change yields no rows while a vulnerability path in a one-line file is kept.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-14 13:24:45 +02:00
Marius CiepluchandClaude Fable 5.1 4cf9c85d22 perf(hunt): the caller search prepares each product file once per change
call_sites rescanned every line of every product file for declarations on each call; with one name per
candidate row that was the whole cost of the source producer. code_index prepares a file once (the names each
line declares), call_sites takes it, and callers_of keeps a cache of prepared files the producer context owns.
Same calls, tested against the unprepared scan. One Laravel change of 33 rows: 249 seconds before this pair of
commits, 20 now.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-14 09:46:46 +02:00
Marius CiepluchandClaude Fable 5.1 86d70831c8 perf(hunt): the source producer walks and reads the product tree once per change, not once per row
facts_for walked the whole product tree and re-read every product file for each candidate row; on a Laravel
application one change of 33 rows took four minutes, 96 percent of it there. The producer context already
holds the product file list and the texts it read for entry points, so facts_for and callers_of now accept
them. Same facts, tested by a call whose tree walk raises and whose file on disk is emptied after reading.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-14 09:35:16 +02:00
Marius CiepluchandClaude Fable 5.1 4063fd1562 feat(semantic): the hunter's semantic producer covers Java and PHP
Java parsed already, but the producer failed every Java row because no obligation facts existed for the
language and that failure took roles and def_use down with it. PHP had no grammar. Now: tree-sitter-php in the
semantic extra with PHP node kinds in the CST reader; obligation facts for Jenkins/Stapler, Spring Security and
Servlet code (java.toml) and for Laravel, WordPress and Symfony (php.toml), each header naming the training
clones and references the names were checked against; a language with an IR but no obligation facts keeps
roles and def_use and records obligations as unchecked (None), which the engineered features treat as missing,
never as zero. Proven on the committed pool: 6 of 6 Java rows and 33 of 33 PHP rows of one fix change each now
carry semantic features. Implemented by Codex; gated here with ruff, mypy and the full suite.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-14 09:35:16 +02:00
Marius CiepluchandClaude Fable 5.1 c8f7610313 fix(types): mypy clean again over src; stub-less libraries and the benchmark imports handled in pyproject
66 errors had accumulated in src since the last green run (the hunt modules of groups 4-6, stats, candidates,
runner). Codex typed them without behaviour change; the 17 inline import-untyped ignores it added are replaced by
one override for dspy, pandas, statsmodels and scikit-learn, and the hunt package's lazy imports of benchmarks/
(never type-checked, not in files) are followed silently so src stays strict against their signatures.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-14 03:03:12 +02:00
Marius CiepluchandClaude Fable 5.1 0d7d3f0dc9 chore: format the whole tree and lint the scratch measurement scripts; CI has been red since 25ae4b4
CI runs ruff format --check and ruff check over the whole repository; 29 files had drifted from the formatter and
four measurement scripts carried lint findings, so every push since 25ae4b4 failed before pytest ran.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-14 02:49:44 +02:00
Marius CiepluchandClaude Fable 5.1 dee23c23f5 measure: both arms over the access-control training pool (2 of 5 pushes, 4 of 48 locations each); runner books real cost
DSPy baseline: 2/5 pushes, 4/48 locations, 14 of 90 ordinary pushes flagged, 25 suspicions (3 true), 11 invalid-request
failures in shopper and trigger.dev, $6.13. Pilot hunter: 2/5 pushes, 4/48 locations, 99 of 100 ordinary pushes
flagged, 594 findings (3 true). Different pushes caught; nothing in trigger.dev.

The runner booked usd 0.0 per change so --budget-usd never stopped a run; it now prices every LM call of the change
at the registered prices and writes usd into the record.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-14 01:14:46 +02:00
Marius CiepluchandClaude Fable 5.1 fcb43fdd47 fix(hunt): an empty product file no longer fails the source producer on every row of a change
The 150 unread rows were not a checkout failure: the entry-point scan read every product file and one empty
file raised empty_source, which escaped the cached property and failed the source producer on every row of
the change (and re-read the whole tree per row). Empty product files are skipped; the row's own file still
has to be non-empty.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-14 00:53:37 +02:00
Marius CiepluchandClaude Fable 5.1 537b61e8d9 fix(hunt): diff producer records where a push removed lines; worktree checkout is proven complete
Two instrument findings from the first access-control feature table (952 rows, 74 positive rows):

1. A fix that adds a guard is, replayed base-to-head, a push that removes it. The diff producer only kept
   hunks with added lines at head, so 38 of 74 positive rows had no hunk in their file at all and 69 of 74
   were "not on a changed line", while 60% of ordinary rows were. The diff annotation separated the labels
   by construction and was blind to exactly the access-control shape. The producer (version 2) now records
   per file the head lines after which the push removed code; distance and on_changed_line count them, the
   model's hunks input renders them as removed_after_line, and diff.deletion_count is an engineered feature.

2. 150 rows (16%, every positive of one repository) read empty files from the worktree although git holds
   the blobs; the build ran while another process checked out from the same clone. _full_checkout now
   proves the checkout: git status must be clean, a dirty tree is restored once, then InstrumentFailure
   checkout_incomplete. The table script cross-checks every unread row against the blob size and refuses
   to screen a partly unread tree, and records the diff-scoped recall ceiling (positive rows a label-blind
   enumeration would reach).

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-13 23:17:15 +02:00
Marius CiepluchandClaude Fable 5.1 499f6c4474 fix(hunt): label every verified target of a change; undo an inherited sparse-checkout in the worktree
label_rows honoured only a change's primary site, so 43 of the 48 accepted access-control
locations were unlabelled; every entry of targets is now a positive with its own label_ref and
span. The training clones were emptied with sparse-checkout to save disk and a new worktree
inherits it: every file read failed and the source and semantic producers reported an unread tree
on all 599 rows. The runner now disables sparse-checkout in the worktree it adds.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-13 21:32:43 +02:00
Marius CiepluchandClaude Fable 5.1 6f9613329e feat(hunt): candidates scored on folds through the runner; the pilot gain, once per accepted program (6.1)
rounds.py runs a candidate over compile or held-out folds through runner.run, so a tool-using
program has its tools bound to each change's checkout, and turns paired records into the
FoldResults accept.decide reads (closes the gap noted in bacd7a1). report.py runs an accepted
program once over the held-out pilot: a ledger line reserved before the run refuses a second run
per program id, contaminated or unchecked training runs are refused, the instrument is proved per
change and more than 5% failures makes the run invalid with no numbers quoted, the pilot's own
scorer and metrics give the after numbers, the recorded hunter numbers give the before, baseline
repeats pair with the single run, the proof arm's workload per 100 pushes is counted, and the gate
distance is read off the confidence curve. 124 hunt and stats tests pass. Implemented by Codex
(gpt-6-astra), gated here.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-13 20:44:18 +02:00
Marius CiepluchandClaude Fable 5.1 bacd7a1e3f feat(hunt): proposals and the acceptance rule (5.1, 5.2)
propose.py: one change per round (instruction, demonstrations or one mask), examples built from
compile folds only with a provenance record of the change ids used, a registry for GEPA, MIPROv2
and BootstrapFewShot (MIPROv2 without the data-aware proposer so labels are never serialised),
compile-fold scoring with Wilson and the repository-paired bootstrap, and a compile-only history
the next round may read. accept.py: the rule as independent clauses (both folds non-negative with
one strictly positive, per-family regression budget on held-out recall, tie tolerance, no
acceptance without an interval, instruction cap, one change only), the comparison recorded in the
candidate artifact's configurations, rejections logged without held-out numbers. 48 fixture tests.
Known gap for live rounds: the optimisers and propose._score call the program directly, so a
tool-using program must be scored through the runner with its tools bound per checkout (6.1).
Implemented by Codex (gpt-6-astra), gated here.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-13 20:22:22 +02:00