fix: align context_peek selector contract with projected schema #376
Closed
nsaspy
wants to merge 3 commits from
fix/312-peek-schema-contract into main
pull from: fix/312-peek-schema-contract
merge into: nsaspy:main
nsaspy:main
nsaspy:feat/prolog-rlm-language
nsaspy:codex/claude-api-provider
nsaspy:codex/symbolic-experts
nsaspy:codex/openai-api-provider
nsaspy:feat/zai-provider-protocols
nsaspy:hardening/project-semantic-20260911
nsaspy:hardening/ws7-nix-ci-pinning
nsaspy:chore/track-prolog
nsaspy:rage/98-semantic-project-knowledge
nsaspy:fix/339-agent-zero-skill-graph
nsaspy:ci/tree-sitter-runner-labels
nsaspy:issue-293
nsaspy:research/text-streaming
nsaspy:docs/agent-editor-handoff
nsaspy:icon-add
nsaspy:hydra/fleet-jobs-20260904
nsaspy:rage/355-d6-11-plan-native-dispatch
nsaspy:cd/nix-installer-action-url
nsaspy:issue-355
nsaspy:rage/97-query-capture-apis
nsaspy:research/expert-direct-tool-projection
nsaspy:rage/96-versioned-syntax-facts
nsaspy:fix/direct-deadline-typing
nsaspy:dogfood/auto-dig-native-tools
nsaspy:feature/rage-feature-freeze
nsaspy:fix/323-native-batch-cardinality
nsaspy:fix/328-direct-user-namespace-text-string
nsaspy:fix/transient-provider-retry-2026-09-01
nsaspy:fix/openrouter-dotted-tool-result-name
nsaspy:rage/325-native-call-isolation
nsaspy:fix/316-original-batch-effect-isolation
nsaspy:fix/313-per-call-preflight
nsaspy:docs/agents-worktree-rule
nsaspy:fix/child-native-capability-narrowing
nsaspy:test/paid-lane-glm-53-flash
nsaspy:docs/spec-seeded-symbolic-plans
nsaspy:rage/290-spec-plan-authority
nsaspy:fix/304-context-peek-selector-contract
nsaspy:fix/298-native-any-json-schema
nsaspy:rage/288-spec-plan-graph-executor
nsaspy:feat/spec-plan-flow-api
nsaspy:prolog-rlm-v1
nsaspy:gpt-5-6-sol-high/questions-for-rlm-prolog-and-lambda-rlm
nsaspy:rage/277-planner-protocol-context
nsaspy:research-approval/rlm-research-026-task-deadlines-20260827135838
nsaspy:research-approval/rlm-research-025-lem-ui-20260827135751
nsaspy:rage/183-live-operator-behavior
nsaspy:agent/127-agentprolog-config
nsaspy:recovery/132-agentprolog-monorepo
nsaspy:rage/223-durable-context-mount-recovery
nsaspy:rage/223-constraint-benchmark-recovery
nsaspy:research-approval/rlm-research-011-managed-context-tool-discovery-20260827054019
nsaspy:feat/research-approval-schema
nsaspy:agent/rrlm-control-plane-research
nsaspy:docs/219-adrrd-review
nsaspy:salvage/231-runtime-status
nsaspy:salvage/231-runtime-status-run
nsaspy:tmp
nsaspy:tmp2
nsaspy:tmp3
nsaspy:rage/245-planner-structural-retry
nsaspy:rage/168-skill-selection-eval
nsaspy:rage/250-skill-catalog-graph
nsaspy:rage/56-result-acceptance
nsaspy:rage/172-parent-resume-replan
nsaspy:rage/175-deadline-policy-recovery
nsaspy:rage/257-provider-tool-choice-normalization
nsaspy:agent/tool-result-projection-presets
nsaspy:fix/234-numeric-schema-bounds
nsaspy:feature/231-rlm-cli-reference-harness
nsaspy:feature/223-durable-context-mounts
nsaspy:feature/223-real-constraint-benchmark
nsaspy:feature/223-real-constraint-benchmark-clean
nsaspy:feature/223-real-constraint-benchmark-final
nsaspy:feature/223-real-constraint-benchmark-impl
nsaspy:feature/223-real-constraint-benchmark-now
nsaspy:feature/223-real-constraint-benchmark-tdd
nsaspy:feature/223-real-constraint-benchmark-work
nsaspy:rage/176-root-planner-tool-projection
nsaspy:rage/172-typed-delegation-policy
nsaspy:rage/175-subagent-deadline-policy
nsaspy:rage/206-prompt-command-runtime
nsaspy:rage/203-subagent-skill-role-provenance
nsaspy:rage/200-permanent-rlm-context
nsaspy:fix/190-cli-help-success
nsaspy:archive/pr-132-agentprolog-config-20260827
nsaspy:feature/117-prolog-skill-activation-linear
nsaspy:feature/117-prolog-skill-activation
nsaspy:fix/194-completion-budget-usage
nsaspy:fix/191-capability-filtered-tool-schemas
nsaspy:fix/185-reasoning-effort
nsaspy:ci/report-workflow-failures-20260825
nsaspy:cleanup/186-remove-legacy-harnesses
nsaspy:fix/185-reasoning-routing
nsaspy:agent/evolution-async-evaluator
nsaspy:rage/181-binding-replay-race
nsaspy:agent/124-deepseek-harness-prolog
nsaspy:rage/144-fallback-closure
nsaspy:rage/164-conversation-metadata
nsaspy:agent/subagent-supervised-call-conformance
nsaspy:fix/160-context-adapter-closed-data
nsaspy:codex/move-agent-zero-adaptor
nsaspy:codex/sol-high-integration
nsaspy:fix/151-plunit-gate
nsaspy:fix/165-evolution-closed-data
nsaspy:fix/162-async-control-exceptions
nsaspy:fix/158-prompt-compiler-closed-dicts
nsaspy:fix/151-plunit-main-ownership
nsaspy:fix/156-registry-destroy-hooks
nsaspy:fix/154-anonymous-dict-canonicalization
nsaspy:fix/flake-lock-reproducibility
nsaspy:agent/issue-142-evolution-kernel
nsaspy:agent/141-flake-runtime-package
nsaspy:agent/rlm-subagent-runtime
nsaspy:agent/144-subagent-fallback-a
nsaspy:agent/107-bound-adapter-metadata
nsaspy:validation/clean-pack-install
nsaspy:fix/45-authoritative-nested-model-events
nsaspy:fix/46-router-safe-live-streaming
nsaspy:agent/prompt-context-compiler
nsaspy:agent/42-canonical-recursive-fingerprints
nsaspy:agent/136-static-load-errors-fail-ci
nsaspy:agent/44-completion-error-usage
nsaspy:fix/67-loader-registry-cleanup
nsaspy:agent/opentui-solid-reference-client-current
nsaspy:agent/95-project-source-registry
nsaspy:feature/issue-117-prolog-skill-compiler
nsaspy:backlog/roadmap-86-merged
nsaspy:agent/opentui-solid-reference-client
nsaspy:79-tool-effect-boundary
nsaspy:agent/prolog-agent-ui-research
nsaspy:agent/conversation-cold-context
nsaspy:agent/94-tree-sitter-ffi
nsaspy:agent/conversation-warm-context
nsaspy:111-opentui-solid-reference-client
nsaspy:109-prolog-agent-ui-v1
nsaspy:agent/conversation-runtime
nsaspy:agent/spec-mode-language
nsaspy:agent/spec-verify-foundation
nsaspy:agent/prompt-compiler-research
nsaspy:reconcile-backlog-postmerge
nsaspy:reconcile-backlog-issues
nsaspy:agent/prolog-agent-roadmap
nsaspy:feature/issue-84-effect-store-migration
nsaspy:fix/issue-80-effect-substrate-adversarial-hardening
nsaspy:feature/issue-57-effect-identity
nsaspy:agent/mcp-declaration-security
nsaspy:agent/external-tool-category-boundary
nsaspy:feature/issue-53-authority-pending-async
nsaspy:feature/issue-54-agent-graph-canonical-async
nsaspy:feature/issue-54-tools-mcp-canonical-async
nsaspy:feature/issue-54-async-canonical-runtime
nsaspy:agent/dual-sync-async-runtime
nsaspy:agent/reconcile-todo-status
nsaspy:feature/issue-20-deep-recursion-experiments
nsaspy:feature/issue-19-cli-demo-trace
nsaspy:feature/issue-18-benchmark-conformance
nsaspy:feature/issue-17-adaptive-recursion
nsaspy:feature/issue-16-durable-artifacts
nsaspy:feature/issue-15-mcp-2026-dual-version
nsaspy:feature/issue-14-mcp-2025-11-25
nsaspy:hotfix/live-tool-native-openrouter
nsaspy:feature/issue-13-chain-runtime
nsaspy:fix/stable-live-repair-gate
nsaspy:fix/live-repair-strategy-parser
nsaspy:feature/issue-12-durable-graph
nsaspy:feature/issue-11-agent-supervision
nsaspy:feature/issue-10-structured-outcomes-repair
nsaspy:feature/issue-9-rlm-completion
nsaspy:feature/issue-8-capability-tools
nsaspy:feature/issue-7-typed-plan-runtime
nsaspy:feature/issue-6-context-store
nsaspy:fix/openrouter-reasoning-response
nsaspy:fix/live-openrouter-smoke-stability
nsaspy:feature/issue-5-openrouter-provider
nsaspy:feature/issue-4-swi-bootstrap
nsaspy:agent/agentic-harness-research
nsaspy:agent/prolog-rlm-foundation
No reviewers
Labels
Clear labels
bug
Something isn't working
documentation
Improvements or additions to documentation
duplicate
This issue or pull request already exists
enhancement
New feature or request
good first issue
Good for newcomers
help wanted
Extra attention is needed
invalid
This doesn't seem right
question
Further information is requested
wontfix
This will not be worked on
No labels
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
wontfix
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set.
Reference
nsaspy/prolog-rlm!376
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/312-peek-schema-contract"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes #312.
Problem
The projected
context_peektool schema and the native direct-mode validator disagreed. Auto-Dig's model-emitted call{"selector":{"count":20,"index":0,"type":"head"}}was rejected in native preflight (unexpected_fields([index])), and the previously inspected PR head still accepted{"type":"metadata","index":-1}through native validation even though the schema advertisesselector.indexwithminimum: 0.Approach: one authoritative local selector contract
prolog/rlm_direct.plnow describes thecontext_peekselector contract once —peek_selector_types/1,peek_selector_fields/1,peek_selector_required/1,peek_selector_minimum/2,peek_selector_default/2,peek_default_count/1— and both consumers derive from it:peek_selector_schema/1, projected throughcontext_schema(peek, ...)) builds the selector object schema (closed field set, requiredtypeenum, integer minima) from those facts;peek_selector/2) reads the same facts for the allowed field set, enum membership, minima, and defaults.A conformance test pins the advertised contract and walks the projected schema to probe native validation in both directions, so schema/validator drift fails the deterministic gate instead of diverging at runtime.
Selector contract
type,index,count.typeis required; enummetadata | head | tail | item.index >= 0,count >= 1. No selector type bypasses the advertised bounds.head/tailwith omittedcountuse the boundedpeek_default_count(128);itemwith omittedindexuses0.metadataignoresindex/countsemantics;head/tailnever treatindexas an offset;itemnever treatscountas slicing.direct_error{phase:schema, kind:malformed_arguments, detail:invalid_selector_field(Field, Minimum)}carrying the advertised minimum; shape violations throwunsupported_selector/invalid_selector/unexpected_fields([...])as before.Regression coverage
index=0,count=1, and minimal{type}shapes, and rejectsindex=-1,count=0(10 accept + 8 reject cases, fault detail asserted against the advertised minimum).head(128)/tail(128)and item toitem(0); defaults co-exist with validated present fields;peek_default_count(128)pinned.minimum - 1is rejected; boundary faults carry the advertised minimum.{"count":20,"index":0,"type":"head"}executes through the real direct loop —context_peekis advertised on the first request, the tool-result observation is returned with"truncated":trueprovingcount:20actually bounded the read (no over-read of the payload) — and the omitted-count default path executes with the full read.Rebase note
Rebased onto current
main(e9b0c18). PR #290 (merged) touches different files; no mechanical or semantic conflict. The replayed commit's duplicate deterministic-corpus entries (main already registers both test files viae474692) were dropped; the corpus inventory diff vsmainis now empty.Verification (local deterministic gate, SWI-Prolog 9.2.9 via the repo flake)
test/check_runtime.pl,test/load_all.pl: exit 0scripts/validate_research_approval.pl: PASS (19 tracked files)scripts/design_gate.pl: ALL CHECKS PASSEDtest/runner_main_ownership_probe.pl,test/run_runner_integrity_tests.pl: OKtest/run_tests.pl: 92 suites, 1092/1092 passed, 0 failed / timeout / blockedbenchmark/run.pl -- deterministic: 16/16 passedbenchmark/run.pl -- deep-experiment: 15/15 passedbin/prolog-rlm.pl -- demo --json+ graph trace smoke: passgit diff --check: cleanNon-goals
rlm_plan.pl) selector validation operates on already-normalized Prolog selectors — separate surface, unchanged.GitHub Actions on this head is the canonical full gate; the credentialed REAL OpenRouter lane is a live-provider check, not the correctness proof for this deterministic native contract.
The rebased PR head still left a hole: the metadata selector validated count when present but not index, so {"type":"metadata","index":-1} passed native validation while the projected schema advertises selector.index with minimum 0. The selector contract is now one authoritative local description in rlm_direct.pl (types, allowed fields, required list, per-field minima, bounded defaults) from which both the projected tool schema (peek_selector_schema/1 via context_schema(peek,...)) and native validation (peek_selector/2) are derived: - metadata/head/tail/item validate index >= 0 and count >= 1 whenever the field is present, with the advertised minimum carried in the invalid_selector_field(Field, Minimum) fault detail - head/tail keep the bounded peek_default_count(128) default for omitted count and never treat index as an offset - item keeps index default 0 and never treats count as slicing - unknown selector fields, unknown types, wrong primitive types, missing type, and non-dict selectors all fail closed Contract tests pin the advertised schema, walk the projected schema to probe native enforcement in both directions, and cover the full selector boundary matrix plus the direct-mode round trip of the exact Auto-Dig shape {count:20,index:0,type:"head"} and the omitted-count default.CI triage note: the failing
Pinned paid OpenRouter integrationlane is the recurring live-provider failure documented in #291, #287, #281, #293, #302, and #307. The failing jobs are live planner/managed-turn tests (real_planner_uses_opaque_context_and_registered_tool_without_fixed_plan→plan_error{tool_result_envelope_field(content,runtime)}, and the 40000-message needle retrieval test), neither of which touches thecontext_peekselector contract or the deterministic corpus changed here.The repository-required deterministic gate is green on this PR:
Deterministic unit and load checkspass, matching the local run (1077/1077, exit 0). The paid lane failure also reproduces on other branches andmain, so it is not introduced by this change; it is tracked by the automated failure reports (this branch: #315).Rebased onto current
mainand completed. Final state:2ce4a5035b(fix/312-peek-schema-contract; commits: replayed255f65d→ corpus-dedup55ad2dc→ contract completion2ce4a50)e9b0c187fdevery_advertised_field_bound_is_natively_enforcedfailed with\+selector_outcome(_{index:-1,type:"metadata"},ok)— i.e.{"type":"metadata","index":-1}was accepted by native validation although the projected schema advertisesindexminimum:0; 9 plunit tests failed in that run (log retained locally). The metadata-index hole was live on the rebased PR head.test/rlm_direct_context_peek_contract_test.pl(26 units: 10 accept + 8 reject boundary instances, 6 fail-closed shapes, defaults incl.peek_default_count(128), 7 schema/validator conformance tests) andtest/rlm_direct_test.plrlm_direct suite (27 units incl. the end-to-end Auto-Dig shape{"count":20,"index":0,"type":"head"}round trip and the omitted-count default round trip) — all green.check_runtime.pl,load_all.pl,validate_research_approval.pl(PASS, 19 files),design_gate.pl(ALL CHECKS PASSED), static load of all live definitions,runner_main_ownership_probe.pl,run_runner_integrity_tests.pl,test/run_tests.pl— 92 suites, 1092/1092 passed, 0 failed/timeout/blocked; fresh-process graph and artifact restart fixtures pass.benchmark/run.pl -- deterministic16/16 passed;benchmark/run.pl -- deep-experiment15/15 passed.MERGEABLEagainstmainat this head (mergeStateStatus reflects CI in flight on the new head).Scope guard: #313/PR #316 (per-call batch recovery/isolation) is not touched;
rlm_plan.pl, effect accounting, budgets, scheduling, retry policy, and PR #290 artifacts are unchanged. The deterministic corpus diff vsmainis empty (duplicate replayed entries removed).Pull request closed