Skip to content

M2: onboarding loop: candidates, review queue, versioned rules, backlog re-parse - #2

Merged
Karthik-Sethu-Raman merged 15 commits into
mainfrom
worktree-m2-onboarding-loop
Sep 21, 2026
Merged

Karthik-Sethu-Raman merged 15 commits into
mainfrom
worktree-m2-onboarding-loop

Conversation

@Karthik-Sethu-Raman

Copy link
Copy Markdown
Owner

What this does

Closes the M2 milestone (spec §15): new appliance → sample capture → candidate rule → human review in the web Review Queue → approved versioned rule live → backlog re-parses, with the full audit trail.

  • Pipeline (R17): parks samples for rule-less fingerprints in onboarding_samples; emits all-status EventEnvelopes to normalized.events (parsed / unparsed / parse_error) keyed by fingerprint; delivery-error callback; autocommit transaction hygiene.
  • ulpf-core: validate_candidate — the deterministic gate (caps/allow-list, per-line parse, held-out match, IP/port sanity, orphan mappings, 20-mutant adversarial probe, hardcoded-literal detection).
  • Onboarding service (new, cold path): prompt/held-out sample split, SLM generation loop (retry temps 0.1/0.3/0.5 with error feedback, JSON fast-path), and the convergent backlog re-parse sweep (R11: column-scoped UPDATE (superseded_by_event_id) — the only mutable cell in the event store).
  • Gateway: rule-lifecycle write path via least-privilege rules_role (approve, approve-with-edits, reject, manual authoring, deactivate/reactivate) + review/audit read endpoints. The M1 HTTP API contract is untouched (additive only).
  • Web: nav shell, Review Queue (hero page with checks grid, previews, mapping editor, override mode), Rules registry + audit trail.
  • Simulator: --mode new-appliance + newapp corpus.
  • Compose: onboarding service + Ollama sidecars under slm-4b / slm-8b profiles (default qwen3:4b).
  • Migrations 003–006: lifecycle roles/grants/constraints/indexes, plus three sequence-grant hotfixes caught live in smoke runs.
  • Smoke asserts A–H (incl. hero loop, manual authoring, replay stability with samples), CI updates, ruling record in docs/m2-rule-lifecycle.md, measured SLM numbers in docs/m2-slm-sanity.md.

Evidence

  • 244 pytest passed; ruff check . clean; web 25/25 vitest + production build
  • scripts/smoke.py → PASS (A–H), twice consecutively on the accumulating DB
  • Linux rehearsal: py3.12-slim container, ruff + full pytest green on the exact final tree

Known residuals

  • SLM hero path is negative on the 8 GB dev box (docs/m2-slm-sanity.md: 0.0 held-out match across corpora). The demo path is manual authoring through the same loop; M4 owns the tier/harness question.
  • First GH Actions run happens on this PR.

Karthik-Sethu-Raman and others added 15 commits September 20, 2026 10:08
…rors, env example)

Co-Authored-By: Claude Code <noreply@anthropic.com>
…hardening

Co-Authored-By: Claude Code <noreply@anthropic.com>
… hardcoding)

validate_candidate runs all eight CHECK_KEYS (caps/allow-list via the M1
gate, per-line parse + IP/port/severity sanity, held-out match rate,
orphan mappings over observed fields, 20-mutant seeded adversarial probe,
hardcoded-literal token diff) and returns a frozen CandidateReport;
report_to_json emits the rules.validation JSONB shape. The bench's
mutation machinery moves verbatim into ulpf_core.mutations (single
definition; bench passes vary_text=True, the probe mutates values only —
patterns may pin free words like DROP).

kv_rule test pattern accepts DROP|ACCEPT: the brief's ACME held-out line
is an ACCEPT event while its own test expects held_out_match_rate 1.0.

Co-Authored-By: Claude Code <noreply@anthropic.com>
…7 txn/callback fixes

Pipeline now ships one EventEnvelope per event (parsed, unparsed,
parse_error) to normalized.events keyed by fingerprint_id — the R17
contract M3's drift service will consume — and captures onboarding
samples for rule-less fingerprints in the SAME transaction as the
normalized rows (grouped per-batch count query, per-fingerprint cap via
ULPF_SAMPLE_CAPTURE_CAP, ON CONFLICT (raw_id) DO NOTHING).

T7 minors: connect_db runs autocommit and persist_batch/persist_normalized
wrap exactly one `with conn.transaction():` block each (zero-event batches
open no txn; failures still propagate to the rollback+seek-back path);
KafkaProducer passes an on_delivery callback that logs delivery failures
at ERROR.

Collector caps: IngestRequest.lines max_length=1000, format_hint and
RawEnvelope.format_hint max_length=64; TCP lines read with a StreamReader
limit so an oversized line closes the connection (README documents the
UDP-drops-event vs TCP-kills-connection asymmetry).

migration 004: GRANT USAGE on onboarding_samples_id_seq to pipeline_role —
the first live sample insert exposed the missing sequence grant (caught by
the Task 4 live check; migration 001 granted the table only).

Co-Authored-By: Claude Code <noreply@anthropic.com>
…fast-path, validation gate

Co-Authored-By: Claude Code <no-reply@anthropic.com>
…SON rules; retry transport errors

Co-Authored-By: Claude Code <no-reply@anthropic.com>
Co-Authored-By: Claude Code <no-reply@anthropic.com>
Co-Authored-By: Claude Code <no-reply@anthropic.com>
…try scoping

Co-Authored-By: Claude Code <no-reply@anthropic.com>
Three-tab shell (Overview / Review Queue / Rules placeholder) on plain
useState — no router, air-gapped. ReviewQueue lists pending_review
candidates with the gate's checks grid (keys verbatim), held-out preview
pairs, mapping editor (edited_mappings), pattern override textarea
(override body), actor/reason inputs, and Approve/Reject with
disabled-while-submitting + error banner; list refetches after every
action. api gains postJson/getRules/getSamples; types mirror the Task 7
contract key-for-key (RuleRow, ValidationReport, SamplesStatus). T10
polish: TraceDrawer fetch now aborts via AbortController.

Co-Authored-By: Claude Code <no-reply@anthropic.com>
The Rules tab is the real registry (Task 9): GET /api/rules with no
status filter, grouped to one summary row per fingerprint (latest
version, status/provenance badges, activated_at). Expanding a row fires
BOTH getRuleHistory (version timeline: version, badge, provenance,
activated_at) and getAudit (the ts - actor - action feed; action
strings from the closed vocabulary render VERBATIM; the history
response's embedded audit array goes unused). Deactivate POSTs
{actor: "anonymous"} to /api/rules/{fp}/deactivate and refetches so
the badge flips. App.tsx swaps the placeholder for the component;
api gains getRuleHistory/getAudit; types gain AuditRow (6 keys, keyed
to the gateway SELECT) + RuleHistoryResponse/AuditResponse.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
…nity numbers

Co-Authored-By: Claude Code <noreply@anthropic.com>
…ocs: m2 ruling record

M2 Task 12 exit gate. smoke.py extends the M1 A-E acceptance smoke with
the M2 onboarding-loop asserts F/G/H: F takes an SLM candidate (or the
ruled hand-authored manual fallback after ULPF_SMOKE_SLM_WAIT_S, the
expected outcome on the default qwen3:4b tier) and G posts the generic
zenwall rule, both through approve -> backlog re-parse verified by
per-run deltas against the reparse_complete audit row; H re-runs the
forced replay with active rules proving raw/normalized/stats/sample
idempotency. CI installs the onboarding requirements and pins
pytest/ruff; web uses npm ci. docs/m2-rule-lifecycle.md records the M2
rulings (grant matrix, topic envelope, version minting, deferred miner)
and the migration-006 sequence-grant completion note the first live
smoke run caught; README hero-loop and gate docs updated.

Two consecutive green smoke runs on the accumulating database (runs C
and D, 8/8 checks each); containerized Linux CI rehearsal green on the
final staged tree.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
@Karthik-Sethu-Raman
Karthik-Sethu-Raman merged commit a8f578d into main Sep 21, 2026
4 checks passed
@Karthik-Sethu-Raman
Karthik-Sethu-Raman deleted the worktree-m2-onboarding-loop branch September 21, 2026 10:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant