Skip to content

Unit 3 - Add persona context-append wrapper with injection delimiter - #4

Merged
srdjan merged 1 commit into
mainfrom
worktree-agent-a347da0f
Apr 19, 2026
Merged

Unit 3 - Add persona context-append wrapper with injection delimiter#4
srdjan merged 1 commit into
mainfrom
worktree-agent-a347da0f

Conversation

@srdjan

@srdjan srdjan commented Apr 19, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds buildSystemPromptWithContext(allocator, ?project_context) to packages/pi/src/expert_persona.zig
  • Null / empty context returns buildSystemPrompt(allocator) byte-for-byte; non-empty context is appended inside fenced PROJECT CONTEXT markers followed by a data-not-instructions persona sentence (prompt-injection guardrail)
  • Landing the persona-side hook now so a future project-context loader (AGENTS.md / CLAUDE.md) has a single drop-in call site; buildSystemPrompt, prologue, and epilogue are untouched

Plan: /Users/srdjans/.claude/plans/study-the-plan-provided-clever-wadler.md (Unit 3 of 5)

Test plan

  • zig build test-expert-app passes (exit 0)
  • zig build passes (exit 0)
  • New tests cover null, empty, and non-empty context paths
  • printf "help\nquit\n" | zigts expert output contains /verify and Registered tools:
  • printf "/meta\nquit\n" | zigts expert output contains "compiler_version"

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

Add buildSystemPromptWithContext so a future project-context loader has a
single drop-in call site. Null or empty context returns the base prompt
byte-for-byte; non-empty context is appended inside fenced PROJECT CONTEXT
markers followed by a data-not-instructions persona sentence so the model
ignores any prompt-injection attempts embedded in workspace files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@srdjan
srdjan merged commit f147a2a into main Apr 19, 2026
@srdjan
srdjan deleted the worktree-agent-a347da0f branch April 19, 2026 23:36
srdjan added a commit that referenced this pull request May 24, 2026
…r sentinel

ValidatedRuntimeContract.inner was a public field with no compile-time
construction gate, so any caller could synthesise a fake "validated"
contract with a ValidatedRuntimeContract{ .inner = ... } literal and
bypass verifyCapabilityMatrix / verifyPolicyHash / verifyArtifactHash
entirely. The "type-level gate" was comment-only.

Carry a `_proof: ValidationProof` field whose function-pointer signature
points at a file-private `opaque SentinelMarker`. External code cannot
name SentinelMarker (not pub), so it cannot declare a function of the
matching signature, so anonymous struct literal coercion has nowhere to
land. The only path that produces a ValidationProof value is the
file-private `validation_proof` constant, and the only paths that wrap
it into a ValidatedRuntimeContract are validate() and the file-private
validatedFromInner helper used by tests in this module.

Move the four internal literal-construction sites in derivePoolingPolicy
tests through validatedFromInner. Two pinning tests record the gate
shape: validatedFromInner installs the canonical marker, and @typeinfo
introspection confirms the function-pointer parameter remains a pointer
to an opaque type (catches a refactor that loosens it to *anyopaque).

Intent: fix-defect
Scope: runtime/contract, security/validation
Decided-Against: heap-allocated handle with non-pub Impl (changes value semantics across server.zig)
Decided-Against: comptime-field sentinel with secret value (bypassable via source reading)
Decided-Against: rename field to _inner with documentation only (no compile-time enforcement)
Refs: 2026-05-23 overall-repo review, Wave 1B/2D P0 #4

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
srdjan added a commit that referenced this pull request Jun 8, 2026
Code-review cleanup follow-ups (#10, #11, #14) plus one fidelity fix (#2).

#10 - extract replayRecordedEntry(state, ctx, entry): the result-parse +
parse-failure divergence heuristic now has a single owner, shared by
makeReplayStub and makeReplayStubWithFallback instead of being copy-pasted.

#14 - add ReplayState.consumeHead(): the fallback trusts a prior peekMatches
and advances without re-checking bounds/names, dropping the redundant nextIO
re-scan and its dead orelse branch.

#2 - add ReplayState.hasEntryAhead(): when a pure call's recorded entry is not
at the head but exists further on, the fallback counts a divergence (structural
drift) before running real, so replay verification still fails closed; a
genuinely-new call with no recording stays silent. modules_all (entry at head)
and validate-external (no entries) are unaffected.

#11 - add trace.unescapeBody(allocator, body) -> {slice, owned}: test_runner
and replay_runner share it for request-body unescaping, replacing the
duplicated unescape-with-guarded-free idiom (the owned flag also retires the
misleading .ptr re-check).

New unit test covers consumeHead/hasEntryAhead. Verified: test-zigts, test-cli
(497), test-examples 23/23, test-zruntime clean; env exclusion and validate
passthrough intact.

Note: #4 (env fallback under restrictive policy) is already moot - env has
replay_pure=false and never falls through. #5 (matched-path arg checking) is
deferred: it needs the recorder's JSValue serializer extracted and reused for
byte-identical arg comparison, with real risk of false divergences against
existing fixtures; better as a dedicated, test-gated change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
srdjan added a commit that referenced this pull request Jun 28, 2026
…xhigh #4/#5)

The audit was added to verify.sh to machine-check the SMT soundness boundary, but
the gate passed silently when the check did not actually run:

- #4: with z3 absent the audit skipped (available=false, zero failures, exit 0)
  and the gate printed "semantics spec gate OK" - the boundary check was a no-op.
- #5: a slow f64-associativity refutation that hit the 15s ceiling returned
  unknown -> inconclusive -> non-fatal, so a law that was never refuted still
  passed the gate with false confidence.

The command stays deliberately lenient (a z3-absent skip or an inconclusive
timeout is non-fatal, so a future genuinely un-refutable law cannot wedge plain
`spec-check --audit`). Strictness goes in the gate, where these findings live:
verify.sh now re-reads `spec-check --audit --json` and REQUIRES a complete audit
(z3 present, refuted == total, inconclusive == 0). A silent skip or timeout now
fails the gate; an explicit ZIGTTP_Z3 opt-out (off/none/0/disable) is honored.

Also bump the audit :timeout 15s -> 30s (6x the measured ~5s assoc search) so the
now-strict gate does not flake on a slow/loaded machine. The semantics hash is
unchanged (the preamble timeout is not hashed).

Verified: all four gate branches (complete pass / z3-absent fail / explicit
opt-out skip / inconclusive fail) behave correctly; real gate passes 4/4 refuted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
srdjan added a commit that referenced this pull request Jul 12, 2026
… caching, Sonnet default, exit codes

Wave 1 of the expert-analysis recommendations (#1, #2, #4, #5, #8).

#1 Make the retry loop information-complete. The `.run_veto` arm now records
the model's draft as an `apply_edit` tool_use and closes it with the compiler
verdict as a tool_result (session-unique id keyed on transcript index). On a
failure the tool_result carries the diagnostic + SQL escalation and the
compiler-authored repair block is persisted as a follow-up system_note, so the
whole retry context lives in the transcript instead of a transient
`extra_user_text` that a mid-repair tool call would erase. Drops `diag_history`
re-accumulation; `.retry_draft` now sends only a short framing nudge. Adds a
regression test proving the failed draft + diagnostic survive a tool interleave.

#2 Raise the per-turn wall-clock timeout 60s -> 300s (the recorded convergence
for complex handlers ran 80-96s) and drop the eval harness's `turn_timeout_ms=0`
override so measurement and production share one options struct.

#4 Add a rolling cache breakpoint on the last message block so the conversation
prefix hits the prompt cache instead of re-paying full input price every
roundtrip.

#5 Flip the default model claude-haiku -> claude-sonnet-4-6 (the measured 7/7
first-draft baseline); Haiku stays available via `--model` and the harness's
ZIGTTP_CODEGEN_MODEL knob. Docs updated.

#8 Outcome-bearing `--print` exit codes: 0 applied/clean, 1 hard error,
2 veto-exhausted, 3 budget, 4 approval-denied. Documented in the user guide.

Also lands the OpenAI honest-demotion doc note (#7, docs portion).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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