Merge pull request 'fix(cognition): restore register axis on pipeline-served turns + Phase 0 of generalization arc' (#110) from feat/generalization-phase0 into main
Reviewed-on: #110
This commit is contained in:
commit
379e1611ac
10 changed files with 582 additions and 28 deletions
|
|
@ -36,13 +36,14 @@ is a CI failure (`.github/workflows/lane-shas.yml`).
|
|||
| --- | --- | --- | --- | --- |
|
||||
| ADR-0092 | `reviewer_registry` | Reviewer registry schema validates + bootstrap entry self-seals | `evals/reviewer_registry/results/v1_dev.json` | `681a2aab5aa4ffd58cd837ce5673c8b2a9545b570117aec3c02726a12f6876e6` |
|
||||
| ADR-0093 | `domain_contract_validation` | All ratified packs satisfy the 9 ADR-0091 contract predicates | `evals/domain_contract_validation/results/v1_dev.json` | `98ace04e3f02bbc5a8ad655bb6593c3f1ee64cb67014f1122fe6c3c85f48d22f` |
|
||||
| ADR-0095 | `miner_loop_closure` | Miner-sourced proposals route through single reviewed teaching path | `evals/miner_loop_closure/results/v1_dev.json` | `9f071733abe7dcacf759f928548ce738fb639af3fd6e4c621a651b306d7e77ce` |
|
||||
| ADR-0095 | `miner_loop_closure` | Miner-sourced proposals route through single reviewed teaching path | `evals/miner_loop_closure/results/v1_dev.json` | `537094fe21d7e6cfbaf42bfc32b82d669fa9bb05a132d2bc93c72b3ceb7762a6` |
|
||||
| ADR-0096 | `fabrication_control_summary` | Phantom endpoints / cross-pack non-bridges / sibling collapses refuse | `evals/fabrication_control/results/v1_summary.json` | `01e1b6b711141f2b4a14551d7df3ea482d8d6dd7b364a25c509f4f8d08cda8a8` |
|
||||
| ADR-0098 | `demo_composition` | Demos compose from shipped modules; no parallel mechanism | `evals/demo_composition/results/v1_dev.json` | `e2ba2314d8768459fb6a8db082a4bbcf4107b5161d869804a4b2a33c3724081a` |
|
||||
| ADR-0098 | `demo_composition` | Demos compose from shipped modules; no parallel mechanism | `evals/demo_composition/results/v1_dev.json` | `f0611a2ce41721dd40767fc6a83a08470d3c7fd7fc8f1ae8ba003abf8a25ec97` |
|
||||
| ADR-0099 | `public_demo` | Public showcase runs deterministically under 30s; all claims supported | `evals/public_demo/results/v1_dev.json` | `7d8ba0dbae9287cfe0bf15d231fa78a75abc627121c14900439293e01e1cc1d3` |
|
||||
| ADR-0104 | `curriculum_loop_closure` | Curriculum-sourced proposals route through single reviewed teaching path | `evals/curriculum_loop_closure/results/v1_dev.json` | `b46d56b2d209172cc3ffaf3776dc8dcfe55093f13587c5cb67372be6dfa23e8d` |
|
||||
| ADR-0104 | `curriculum_loop_closure` | Curriculum-sourced proposals route through single reviewed teaching path | `evals/curriculum_loop_closure/results/v1_dev.json` | `cb94ca0042d78ec2624129ff6493d52e767b69feea32d2997b85d88f1c0883af` |
|
||||
| ADR-0131 | `math_teaching_corpus_v1` | Math teaching corpus replays deterministically; all chains pass exit criterion (correct_rate=1.0, wrong=0) | `evals/math_teaching_corpus/v1/report.json` | `eaf160d145da29f9050ede8d58bf111b0f651dd40aeae9201857d0b97e014dd4` |
|
||||
| ADR-0206 | `deductive_logic_v1` | Propositional entailment scored against an independent truth-table oracle; dev+holdout+external 716/716 correct, wrong=0, refused=0 | `evals/deductive_logic/report.json` | `97a230949016e38d5e3f37a69e4245b320575ee70e5af92ff7607f7b05f74b5f` |
|
||||
| ADR-0256 | `deduction_serve_v1` | Flag-gated deduction serving decides real English/member/fused arguments end-to-end under earned SERVE licenses; wrong=0 across all splits | `evals/deduction_serve/report.json` | `b530ed99c414a6d1bdd239752b0e1c6c8c03893f402198def29122b9c2a8a8be` |
|
||||
|
||||
## Verification
|
||||
|
||||
|
|
|
|||
|
|
@ -486,6 +486,11 @@ class CognitiveTurnPipeline:
|
|||
grounding_provenance=grounding_src,
|
||||
)
|
||||
surface = resolved.surface
|
||||
# Truth-path bytes for trace_hash (ADR-0069 inv C): the served
|
||||
# surface carries register R6/R4 transforms and MUST NOT move the
|
||||
# hash; hash_surface is the register-invariant canonical-precedence
|
||||
# capture, kept in lockstep with every served-surface mutation below.
|
||||
hash_surface = resolved.hash_surface or resolved.surface
|
||||
articulation_surface = resolved.articulation_surface
|
||||
authority_source = resolved.authority
|
||||
|
||||
|
|
@ -518,6 +523,7 @@ class CognitiveTurnPipeline:
|
|||
if logos_blocks_certified_answer(logos_decision):
|
||||
refusal = decision_as_coherence_refusal(logos_decision)
|
||||
surface = refusal.surface_message or refusal.message
|
||||
hash_surface = surface
|
||||
articulation_surface = surface
|
||||
authority_source = "logos_morph_constraint"
|
||||
except Exception:
|
||||
|
|
@ -667,6 +673,7 @@ class CognitiveTurnPipeline:
|
|||
# proposal (if any) is added below for FUTURE turns to see.
|
||||
if self._speculative_subjects and surface and self._should_mark_speculative(text, surface):
|
||||
surface = _SPECULATIVE_SURFACE_MARKER + surface
|
||||
hash_surface = _SPECULATIVE_SURFACE_MARKER + hash_surface
|
||||
articulation_surface = _SPECULATIVE_SURFACE_MARKER + articulation_surface
|
||||
|
||||
# 10. TEACHING — correction capture, review, and store
|
||||
|
|
@ -705,9 +712,11 @@ class CognitiveTurnPipeline:
|
|||
if len(tok) >= 4 and tok not in _SUBJECT_STOPWORDS:
|
||||
self._forget_speculative_subject(tok)
|
||||
|
||||
# Advance turn counter and remember surface for next correction binding
|
||||
# Advance turn counter and remember surface for next correction binding.
|
||||
# The truth-path surface (not the served, register-decorated bytes)
|
||||
# feeds correction binding so register packs cannot perturb teaching.
|
||||
self._turn_number += 1
|
||||
self._prior_surface = surface
|
||||
self._prior_surface = hash_surface
|
||||
|
||||
# 11. TRACE — deterministic hash (includes teaching IDs and any
|
||||
# typed-operator invocation per ADR-0018).
|
||||
|
|
@ -787,7 +796,7 @@ class CognitiveTurnPipeline:
|
|||
trace_hash = compute_trace_hash(
|
||||
input_text=text,
|
||||
filtered_tokens=filtered_tokens,
|
||||
surface=surface,
|
||||
surface=hash_surface,
|
||||
walk_surface=response.walk_surface,
|
||||
articulation_surface=articulation_surface,
|
||||
dialogue_role=str(response.dialogue_role),
|
||||
|
|
|
|||
|
|
@ -24,8 +24,6 @@ from typing import TYPE_CHECKING
|
|||
from core.cognition.fail_closed import (
|
||||
CoherenceRefusal,
|
||||
ContractViolation,
|
||||
FailureClass,
|
||||
ResidualState,
|
||||
contract_assessment_none_violation,
|
||||
open_geometry_refusal,
|
||||
)
|
||||
|
|
@ -86,6 +84,15 @@ class SurfaceResolution:
|
|||
refusal: CoherenceRefusal | None = None
|
||||
contract_violation: ContractViolation | None = None
|
||||
proof_trace: ProofTrace | None = None
|
||||
hash_surface: str = ""
|
||||
"""Truth-path surface for ``trace_hash`` folding (ADR-0069 inv C).
|
||||
|
||||
``surface`` carries the *served* bytes (post register R6/R4);
|
||||
``hash_surface`` carries the register-invariant truth-path bytes
|
||||
(canonical-precedence base plus the same substrate/fold suffixes).
|
||||
Empty means the served surface IS the truth-path surface (refusal,
|
||||
abstention, and legacy callers) — the pipeline falls back to
|
||||
``surface`` when folding the trace."""
|
||||
|
||||
|
||||
def _base_runtime_surface(
|
||||
|
|
@ -95,25 +102,44 @@ def _base_runtime_surface(
|
|||
response_surface: str,
|
||||
response_articulation_surface: str,
|
||||
) -> tuple[str, str, str]:
|
||||
"""Select the runtime-owned base surface by declared precedence."""
|
||||
"""Select the runtime-owned base surface by declared precedence.
|
||||
|
||||
if canonical_surface:
|
||||
return canonical_surface, response_articulation_surface, "runtime_canonical"
|
||||
``response_surface`` is the runtime's final *served* bytes — post
|
||||
realizer-guard, post substantive register (ADR-0077 R6), post seeded
|
||||
decoration (ADR-0071 R4) — and always wins when present.
|
||||
``canonical_surface`` / ``pre_decoration_surface`` are truth-path
|
||||
identity captures the pipeline folds into ``trace_hash``; they are
|
||||
fallbacks for callers that never sealed a response surface, never a
|
||||
substitute for served bytes. Preferring canonical here strips the
|
||||
entire register axis from pipeline-served turns (terse/convivial
|
||||
stop differing from neutral) while trace_hash stays green — the
|
||||
register-tour claims are the falsifiable contract that catches it.
|
||||
"""
|
||||
|
||||
if response_surface:
|
||||
return response_surface, response_articulation_surface, "runtime"
|
||||
if pre_decoration_surface:
|
||||
return pre_decoration_surface, response_articulation_surface, "runtime_pre_decoration"
|
||||
return response_surface, response_articulation_surface, "runtime"
|
||||
if canonical_surface:
|
||||
return canonical_surface, response_articulation_surface, "runtime_canonical"
|
||||
return "", response_articulation_surface, "runtime"
|
||||
|
||||
|
||||
def _assessment_residual(
|
||||
contract_assessment: "ContractAssessment | None",
|
||||
) -> ResidualState | None:
|
||||
if contract_assessment is None:
|
||||
return ResidualState(detail="contract_assessment is None")
|
||||
return ResidualState(
|
||||
missing_bindings=tuple(contract_assessment.missing_bindings),
|
||||
unresolved_hazards=tuple(contract_assessment.unresolved_hazards),
|
||||
detail=str(contract_assessment.explanation or ""),
|
||||
)
|
||||
def _truth_path_base(
|
||||
*,
|
||||
canonical_surface: str,
|
||||
pre_decoration_surface: str,
|
||||
response_surface: str,
|
||||
) -> str:
|
||||
"""Register-invariant base folded into ``trace_hash``.
|
||||
|
||||
Canonical-first: the composer's pre-R6 capture is the truth-path
|
||||
identity field, byte-identical across register packs (ADR-0069
|
||||
inv C / ADR-0077). Falls through to pre-decoration, then the
|
||||
response itself for turns that never captured a canonical surface.
|
||||
"""
|
||||
|
||||
return canonical_surface or pre_decoration_surface or response_surface
|
||||
|
||||
|
||||
def _abstention_resolution(
|
||||
|
|
@ -211,9 +237,17 @@ def _grounded_open_hedge_resolution(
|
|||
response_surface=response_surface,
|
||||
response_articulation_surface=response_articulation_surface,
|
||||
)
|
||||
truth_base = _truth_path_base(
|
||||
canonical_surface=canonical_surface,
|
||||
pre_decoration_surface=pre_decoration_surface,
|
||||
response_surface=response_surface,
|
||||
)
|
||||
surface = (
|
||||
f"{_GROUNDED_OPEN_HEDGE_PREFIX} {base_surface}" if base_surface else base_surface
|
||||
)
|
||||
hash_surface = (
|
||||
f"{_GROUNDED_OPEN_HEDGE_PREFIX} {truth_base}" if truth_base else truth_base
|
||||
)
|
||||
articulation = (
|
||||
f"{_GROUNDED_OPEN_HEDGE_PREFIX} {base_articulation}"
|
||||
if base_articulation
|
||||
|
|
@ -229,6 +263,7 @@ def _grounded_open_hedge_resolution(
|
|||
refusal=None,
|
||||
contract_violation=None,
|
||||
proof_trace=None,
|
||||
hash_surface=hash_surface,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -322,6 +357,11 @@ def resolve_surface(
|
|||
response_surface=response_surface or "",
|
||||
response_articulation_surface=response_articulation_surface or "",
|
||||
)
|
||||
hash_surface = _truth_path_base(
|
||||
canonical_surface=canonical_surface or "",
|
||||
pre_decoration_surface=pre_decoration_surface or "",
|
||||
response_surface=response_surface or "",
|
||||
)
|
||||
|
||||
# === DUAL-COMPETING SHADOW COHERENCE GATE ===
|
||||
# Forward and conjugate evaluated as independent competitors; commit
|
||||
|
|
@ -340,6 +380,7 @@ def resolve_surface(
|
|||
|
||||
if not gate_blocks and realized_surface and forward_ok and conjugate_ok:
|
||||
surface = realized_surface
|
||||
hash_surface = realized_surface
|
||||
articulation_surface = realized_surface
|
||||
authority = "substrate_realizer"
|
||||
elif (
|
||||
|
|
@ -352,12 +393,16 @@ def resolve_surface(
|
|||
# Transitional shim: geometric coherence holds, but graph not yet
|
||||
# fully grounded. Never used when conjugate residual fails.
|
||||
surface = realized_surface
|
||||
hash_surface = realized_surface
|
||||
articulation_surface = realized_surface
|
||||
authority = "realizer"
|
||||
|
||||
fold_sources: list[str] = []
|
||||
if walk_surface:
|
||||
surface = f"{surface} — {walk_surface}" if surface else walk_surface
|
||||
hash_surface = (
|
||||
f"{hash_surface} — {walk_surface}" if hash_surface else walk_surface
|
||||
)
|
||||
articulation_surface = (
|
||||
f"{articulation_surface} — {walk_surface}"
|
||||
if articulation_surface
|
||||
|
|
@ -367,6 +412,9 @@ def resolve_surface(
|
|||
|
||||
if compose_surface:
|
||||
surface = f"{surface} — {compose_surface}" if surface else compose_surface
|
||||
hash_surface = (
|
||||
f"{hash_surface} — {compose_surface}" if hash_surface else compose_surface
|
||||
)
|
||||
articulation_surface = (
|
||||
f"{articulation_surface} — {compose_surface}"
|
||||
if articulation_surface
|
||||
|
|
@ -392,6 +440,7 @@ def resolve_surface(
|
|||
refusal=None,
|
||||
contract_violation=None,
|
||||
proof_trace=proof,
|
||||
hash_surface=hash_surface,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
# Handoff Brief — Tier O (Opus 5, MEDIUM risk)
|
||||
|
||||
**Read first:** `docs/plans/generalization-arc-2026-07-24.md` (the plan of
|
||||
record — §4 gold contract and §6 constraint set are binding), then
|
||||
`AGENTS.md`. Reference ADRs on demand: 0256–0259 (band recipe),
|
||||
0175/0199 (license doctrine), 0251/0252 (math-reader guardrails).
|
||||
|
||||
**You own, in order:**
|
||||
|
||||
1. **O1 — Band v6-EX (existential `some` witnesses).** Follow the band
|
||||
recipe exactly as ADR-0258/0259 executed it: reader module in
|
||||
`generate/proof_chain/`, shape bands in `shape.py`, surface wiring in
|
||||
`chat/deduction_surface.py`, arena templates ≥720/band in
|
||||
`evals/deduction_serve/practice/gold.py`, hand-authored lane split,
|
||||
ledger reseal, tests, ADR, research doc. Gotchas that bit before:
|
||||
cross-module `_Reject` identity (catch both classes); band-N swallows
|
||||
band-N+1 test texts (verify the prior band really refuses your
|
||||
declined cases); surgical single-line pin edits only (never
|
||||
`verify_lane_shas.py --update`).
|
||||
2. **O2 — Phase 2 physics serve lane** per the §4 curriculum-entailment
|
||||
contract. Premises come ONLY from ratified domain chains
|
||||
(`teaching/domain_chains/physics_chains_v1.jsonl` + pack mounts);
|
||||
anti-recall probes (gold=UNKNOWN for true-but-untaught) are mandatory
|
||||
or the lane must not ship. Independent closed-world oracle first,
|
||||
then the lane. Biology second, only after O3 lands.
|
||||
3. **O3 — generic consumption bridge**: extract the seal→ratify→
|
||||
SHA-verified-license→serve-gate pattern (models:
|
||||
`chat/deduction_serve_license.py`,
|
||||
`generate/determine/estimation_license.py`) into a reusable ADR-0175
|
||||
Phase-5 bridge so subject arenas plug in without bespoke wiring.
|
||||
4. **O4 — math reader: seeding-sentence injection** (standing ruling
|
||||
#77 — the 75% "no injection" wall). Then compare unblock (4.2).
|
||||
Hard guardrails: no bespoke-per-case regex growth (ADR-0251); check
|
||||
every new pattern against
|
||||
`docs/research/reader-arc-overfit-inventory-2026-07-19.md`; measure
|
||||
on the frozen tune/measure split; wrong=0 on the full 500 is the
|
||||
gate, parse-rate gain is the goal.
|
||||
|
||||
**Constraints (non-negotiable):** wrong=0; all flags stay default-off —
|
||||
flips are Shay's ratification only; Forgejo-primary (no `gh`); worktree
|
||||
per unit off `forgejo-https/main`; fresh venv per worktree
|
||||
(`rm -rf .venv && uv sync --locked --offline`, then `uv run --no-sync`);
|
||||
pre-push hook runs smoke+warmed_session (~4.5 min — budget timeouts);
|
||||
PRs opened by Shay via the compare URL the push prints (push PAT 403s
|
||||
the PR API); Shay merges — never you; branch+worktree deleted after
|
||||
merge; no timelines in any doc.
|
||||
|
||||
**Your exit checkpoint (O→S):** v6-EX merged; ≥1 subject lane serving
|
||||
wrong=0 with earned licenses; bridge merged; math increment measured and
|
||||
documented (a null result is a valid checkpoint). Then update
|
||||
`BRIEF-S-sonnet-low-tier.md` with actual state, update memory, and stop
|
||||
for the Sonnet handoff.
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
# Handoff Brief — Tier S (Sonnet 5, LOW risk)
|
||||
|
||||
**Read first:** `docs/plans/generalization-arc-2026-07-24.md` §6
|
||||
(constraint set is binding) and the Tier-O brief's constraints block —
|
||||
they apply verbatim. Opus will have updated this brief with actual state
|
||||
at its exit checkpoint; trust that update over anything stale below.
|
||||
|
||||
**You own (independent items, any order unless noted):**
|
||||
|
||||
1. **S1 — ratification packet for `deduction_serving_enabled`.**
|
||||
Evidence collation only — the flip is Shay's decision, never yours.
|
||||
Contents: 17+-band sealed ledger stats
|
||||
(`chat/data/deduction_serve_ledger.json`), serve-lane results
|
||||
(`evals/deduction_serve/report.json`), byte-identical-when-off proof
|
||||
(cite ADR-0256 + `tests/test_deduction_surface.py` flag tests), blast
|
||||
radius (exact dispatch point `chat/runtime.py` deduction branch), and
|
||||
rollback (flip back, zero residue). One markdown doc in
|
||||
`docs/research/`, PR'd.
|
||||
2. **S2 — lane re-pins + flake documentation.** Only AFTER the Phase-0.1
|
||||
drift findings doc exists and says re-pin is safe. Surgical
|
||||
single-line edits in `scripts/verify_lane_shas.py` — NEVER `--update`
|
||||
(it rewrites every lane and silently drops erroring lanes' pins).
|
||||
Document the `public_demo` env-timeout flake where the findings doc
|
||||
says.
|
||||
3. **S3 — vocab-trigger instrument.** Implement the measurable test
|
||||
specified in `docs/handoffs/COMPREHENSION-READER-AUDIT.md`
|
||||
(§measurable-test, lines ~163–166 and ~231–238): refusal histogram
|
||||
split mechanism-vs-coverage + Phase-2-admissions-per-lexicon-batch
|
||||
counter. CLI or lane runner per the spec; do not invent policy — the
|
||||
spec decides, you implement.
|
||||
4. **S4 — HITL proposal-queue CLI.** A `core` CLI surface listing +
|
||||
reviewing pending proposals from the existing sinks
|
||||
(`teaching/proposals/`, contemplation/idle sinks). Read + review-state
|
||||
transitions only; NO ratification automation, NO corpus mutation, NO
|
||||
flag flips.
|
||||
5. **S5 — housekeeping.** Promotion sweeps Opus's checkpoint notes call
|
||||
for; capability-index entries for new subject lanes; docs/memory
|
||||
updates; dead-code removal only when unambiguous.
|
||||
|
||||
**Constraints:** identical to Tier O (worktrees, fresh venv, pre-push
|
||||
gate, compare-URL PRs, Shay merges, wrong=0, flags stay off, no
|
||||
timelines). When any item's scope turns out larger than described here —
|
||||
stop and flag it in the PR/handoff notes rather than improvising.
|
||||
|
||||
**Arc close:** all Tier-S PRs pushed; memory updated; note whether the
|
||||
Phase-5 articulation trigger (multi-step decided content exists) has
|
||||
fired, for the next arc's scoping.
|
||||
238
docs/plans/generalization-arc-2026-07-24.md
Normal file
238
docs/plans/generalization-arc-2026-07-24.md
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
# Generalization Arc — GSM8K-Level Capability Across Core Subjects
|
||||
|
||||
**Date:** 2026-07-24 · **Status:** ACTIVE · **Base:** main @ `5224b5e0` (post ADR-0259 / Band v4-CM)
|
||||
|
||||
This is the ratified plan of record for the arc that takes CORE from "one
|
||||
fully-closed cognitive lifecycle (deduction-serve)" to "the same lifecycle
|
||||
running across core subjects at GSM8K-exam level" — articulated answers,
|
||||
problem solving, comprehension, all under wrong=0 discipline.
|
||||
|
||||
Execution is **risk-tiered across three operators** (§6): Fable 5 does the
|
||||
high-risk design + build, Opus 5 the medium tier, Sonnet 5 the low tier.
|
||||
Handoff briefs live in `docs/handoff/generalization-2026-07/`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Ground truth at arc start
|
||||
|
||||
- Exactly one lifecycle is closed end-to-end: **deduction-serve**
|
||||
(comprehend → 5 band readers → ROBDD decide → sealed practice arena,
|
||||
17 bands × 720, θ_SERVE=0.99 → SHA-verified ratified ledger → license
|
||||
gate → render → register chain). Dark behind
|
||||
`deduction_serving_enabled=False`, awaiting ratification.
|
||||
- Cross-subject substrate exists but is idle: 16 unmounted domain seed
|
||||
packs, 5 domain chain corpora, 6 OOD fluency lanes, five-layer
|
||||
teaching-order doctrine (`docs/teaching_order.md`), capability index.
|
||||
- Learning-loop organs exist but are write-only or dark: 19 default-off
|
||||
flags; proposal/contemplation/promotion paths emit into review sinks
|
||||
with no runtime consumer. Three real ratified-artifact consumers exist
|
||||
(deduction ledger, estimation license, recognizer registry) — the
|
||||
pattern to generalize.
|
||||
- Math is reader-gated, not solver-gated: compiler holds 50/50 wrong=0 on
|
||||
dev-1 but the reader parses ~1% of real GSM8K; the ~30-band bet was
|
||||
falsified; standing ruling #77 selects **seeding-sentence injection**
|
||||
as the next math move.
|
||||
- ADR-0246 §3.7 identity calibration is explicitly OFF this critical
|
||||
path (blocked on §11 research evidence; doctrine order:
|
||||
capability → practice → calibration → serve).
|
||||
|
||||
## 2. Phases
|
||||
|
||||
### Phase 0 — Truth-substrate hygiene + first live serve *(Small)*
|
||||
- **0.1** Root-cause the 3-lane pin drift (`miner_loop_closure`,
|
||||
`curriculum_loop_closure`, `demo_composition`). Classify: determinism
|
||||
bug (stop-the-line) vs stale pins vs local/CI byte divergence.
|
||||
Re-pin surgically only after root-cause. Findings doc in
|
||||
`docs/research/`.
|
||||
- **0.2** Ratification packet for `deduction_serving_enabled` (evidence
|
||||
collation only; the flip is Shay's decision).
|
||||
|
||||
**Done when:** drift explained + resolved; packet delivered.
|
||||
|
||||
### Phase 1 — Reading generalization: verb predicates + existentials *(Large)*
|
||||
The band cascade reads only copula sentences; every other subject states
|
||||
facts as verb sentences. Ordered by leverage:
|
||||
|
||||
- **1.1 Band v5-VP — verb predicates**: predicate atoms keyed by
|
||||
(verb lemma, argument tuple); intransitive + transitive with
|
||||
named/classed arguments ONLY; closed morphology; typed refusal for
|
||||
everything else (voice, tense shifts, ditransitives, PP-arguments).
|
||||
This band gates Phase 2.
|
||||
- **1.2 Band v6-EX — existential (`some`) witnesses**: completes the
|
||||
all/no/some square; witness-based lowering.
|
||||
- Deferred (unchanged from ADR-0259 §5): universal-nested-in-connective
|
||||
(bound-variable tracking), identity/co-reference, tense.
|
||||
|
||||
Recipe per band (proven 3×): reader module → shape bands → arena
|
||||
templates (≥720/band) → hand-authored lane split (content-disjoint from
|
||||
practice lexicon) → ledger reseal → tests → ADR → research doc.
|
||||
|
||||
**Done when:** verb-predicate and existential arguments decided at SERVE
|
||||
wrong=0; ledger resealed; promotion sweep of previously-declined cases
|
||||
done honestly (promotion = *decided correctly*, not *decided favorably*).
|
||||
|
||||
### Phase 2 — Port the lifecycle to two non-math subjects *(Medium-Large)*
|
||||
Physics and biology first (OOD lanes, seed packs, and domain chains
|
||||
already exist). Implemented strictly to the **curriculum-entailment gold
|
||||
contract** (§4 — the load-bearing design of this arc):
|
||||
|
||||
- Mount subject packs; wire domain chains through the teaching corridor
|
||||
per the five-layer order.
|
||||
- Exam-shaped serve lanes: question → read (v5-VP reading) → decide
|
||||
**from ratified curriculum premises only** → licensed articulated
|
||||
answer.
|
||||
- Per-subject band ledgers via the same arena; capability-index
|
||||
coverage entries; 3-domain anti-overfit panel discipline
|
||||
(independence must be in the READING).
|
||||
|
||||
**Done when:** two subjects serve wrong=0 on hand-authored exam-shaped
|
||||
lanes with earned licenses, zero subject-specific engine code.
|
||||
|
||||
### Phase 3 — Close the discovery→learning loop *(Medium)*
|
||||
- **3.1** Vocab-expansion trigger as code (the COMPREHENSION-READER-AUDIT
|
||||
§measurable-test, made a standing instrument: refusal histogram split
|
||||
mechanism-vs-coverage; admissions-per-lexicon-batch).
|
||||
- **3.2** HITL proposal-queue surface (review CLI over the existing
|
||||
sinks).
|
||||
- **3.3** Generic consumption bridge: extract the seal → ratify →
|
||||
SHA-verified license → serve-gate pattern (deduction ledger is the
|
||||
model; estimation license the second instance) into the ADR-0175
|
||||
Phase-5 bridge so each subject arena plugs in without bespoke wiring.
|
||||
- **3.4** Feed discovery-yield with real traffic (post 0.2 flip).
|
||||
|
||||
**Done when:** a serve-time gap flows discovery → proposal →
|
||||
ratification → runtime consumption with no manual file surgery.
|
||||
|
||||
### Phase 4 — Math problem-solving reader *(Medium-Large, parallel lane)*
|
||||
Independent of Phases 1–3 (no shared files; production-line pattern):
|
||||
|
||||
- **4.1** Seeding-sentence injection (ruling #77; the 75% "no injection"
|
||||
wall).
|
||||
- **4.2** Compare unblock (summation-question reader + inverse compare),
|
||||
queued behind 4.1 per the same ruling.
|
||||
- **4.3** q:complex decomposition study (increment-1 band plan §9; 101
|
||||
cases behind one label).
|
||||
- Guardrails pinned: `docs/research/reader-arc-overfit-inventory-2026-07-19.md`,
|
||||
ADR-0251 recalibration (no bespoke-per-case regex growth),
|
||||
ADR-0252.
|
||||
|
||||
**Done when:** real-GSM8K parse floor materially above ~1% with wrong=0
|
||||
held on the full 500; ceiling revised with evidence.
|
||||
|
||||
### Phase 5 — Articulation depth (`generate/` "core_logos") *(trigger-gated)*
|
||||
Not queued by order. Trigger: Phases 1–2 produce multi-step decided
|
||||
content (proof chains, multi-fact answers) — the evidence of where
|
||||
articulation quality binds that the intelligence-loop plan's Phase 6
|
||||
deferral was waiting for. Scope the arc from that evidence.
|
||||
|
||||
## 3. Dependencies
|
||||
|
||||
- Phase 1.1 gates Phase 2. Phase 0 first.
|
||||
- Phase 3.3 lands before the *second* subject arena (avoid copy-paste
|
||||
wiring). 3.1/3.2 interleave freely.
|
||||
- Phase 4 fully parallel. Phase 5 trigger-gated on 1–2.
|
||||
- §3.7 identity calibration stays downstream of the whole arc (its
|
||||
calibration data comes *from* this arc's practice volume).
|
||||
|
||||
## 4. Curriculum-entailment gold contract (Phase-2 design, decided here)
|
||||
|
||||
This section is the HIGH-risk design, fixed now so the medium tier can
|
||||
implement without re-litigating epistemology.
|
||||
|
||||
1. **Verdict domain** — every exam item resolves to
|
||||
{entailed, refuted, unknown, declined}, identical to deduction-serve.
|
||||
UNKNOWN is the honest verdict for untaught facts. No open-world
|
||||
recall, ever.
|
||||
2. **Two question shapes** — (a) self-contained arguments (premises in
|
||||
the text): already owned by bands v2–v6; (b) **curriculum-grounded
|
||||
questions** (the new Phase-2 shape): question text supplies only the
|
||||
query; the premise set is compiled from the RATIFIED domain chain
|
||||
corpus for that subject. Gold is a function of (curriculum, question)
|
||||
— never of case-local hidden text. This is the decoding-not-generating
|
||||
line in mechanical form.
|
||||
3. **Premise provenance** — each lane case pins the chain IDs it draws
|
||||
on; the runner reconstructs premises from the ratified corpus and
|
||||
MUST fail the case if a pinned chain is absent or unratified.
|
||||
4. **Independent oracle** — per subject, a closed-world reachability
|
||||
oracle over the chain corpus (sharing no code with the serving path)
|
||||
validates every gold verdict; the lane asserts corpus soundness
|
||||
before any case runs (the `assert_corpus_sound()` pattern).
|
||||
5. **License granularity** — bands keyed by (subject × relation family ×
|
||||
chain depth), earned in the ADR-0199 arena: θ_SERVE=0.99, n≥720,
|
||||
wrong=0, sealed + SHA-verified ledger per subject.
|
||||
6. **Typed refusals, lifecycle-mapped** — `untaught_vocabulary`,
|
||||
`unratified_chain`, `out_of_curriculum`, `ambiguous_reading`; the
|
||||
OOV refusal feeds the Phase-3 proposal queue (refusals become
|
||||
discovery, not dead ends).
|
||||
7. **Anti-recall probes (mandatory per lane)** — cases whose answer is
|
||||
true in the world but absent from the curriculum; gold = UNKNOWN.
|
||||
A lane without these probes cannot prove the system decodes rather
|
||||
than recalls, and MUST NOT ship.
|
||||
|
||||
## 5. Risks
|
||||
|
||||
- **HIGH — subject-gold epistemology drift**: mitigated by §4 (contract
|
||||
fixed before implementation; anti-recall probes mandatory).
|
||||
- **HIGH — v5-VP scope creep**: argument structure/voice/agreement are a
|
||||
real jump from copula. Mitigation: two argument shapes only, closed
|
||||
morphology, typed refusal for the rest — the same discipline that made
|
||||
v2–v4 land clean.
|
||||
- **MEDIUM — arena scale**: 720/band × growing band count on local
|
||||
hardware; linear sealing time; watch, don't block.
|
||||
- **MEDIUM — synthetic-overfit trap**: practice templates and lane cases
|
||||
stay content-disjoint per subject (standing doctrine).
|
||||
- **MEDIUM — q:complex may not decompose**: nothing gates on Phase 4.
|
||||
- **LOW — drift root-cause escalates**: if Phase 0.1 finds a determinism
|
||||
bug, the arc pauses until it is fixed (stop-the-line).
|
||||
|
||||
## 6. Risk-tiered operator assignment
|
||||
|
||||
Constraint set for every tier: wrong=0; flags stay default-off (flips
|
||||
are Shay's ratification only); Forgejo-primary, no GitHub/`gh`; local
|
||||
smoke + warmed_session gate before every push (pre-push hook); `uv`
|
||||
always; branch + worktree per unit; no merge automation; one PR per
|
||||
coherent solution; attribution disabled.
|
||||
|
||||
### Tier F — Fable 5 (HIGH risk; this session)
|
||||
- F1: Phase 0.1 drift root-cause (+ surgical re-pin if benign).
|
||||
- F2: this plan doc + handoff briefs.
|
||||
- F3: §4 gold contract (done — it is this document).
|
||||
- F4: Band v5-VP (Phase 1.1), full recipe, PR.
|
||||
|
||||
**Checkpoint F→O:** F1/F2/F4 PRs pushed with compare URLs; memory
|
||||
updated; briefs current. Opus starts at O1 with no open design
|
||||
questions.
|
||||
|
||||
### Tier O — Opus 5 (MEDIUM risk)
|
||||
- O1: Band v6-EX existentials (Phase 1.2) — recipe + ADR-0257/0258/0259
|
||||
as references.
|
||||
- O2: Phase 2 physics + biology serve lanes per §4 (mount packs, wire
|
||||
chains, arena, ledgers, lanes). Physics first; biology second only
|
||||
after the generic bridge (O3) lands.
|
||||
- O3: Phase 3.3 generic consumption bridge.
|
||||
- O4: Phase 4.1 seeding-sentence injection, then 4.2; 4.3 as analysis.
|
||||
Guardrail docs pinned in §2/Phase 4.
|
||||
|
||||
**Checkpoint O→S:** v6-EX merged; ≥1 subject lane serving wrong=0 with
|
||||
earned licenses; bridge merged; math increment measured + documented
|
||||
(whatever its outcome — a null result is a valid checkpoint).
|
||||
|
||||
### Tier S — Sonnet 5 (LOW risk)
|
||||
- S1: Phase 0.2 ratification packet (evidence collation).
|
||||
- S2: Post-fix lane re-pins; public_demo flake documentation.
|
||||
- S3: Phase 3.1 vocab-trigger instrument (spec is pinned in
|
||||
`docs/handoffs/COMPREHENSION-READER-AUDIT.md` §measurable-test).
|
||||
- S4: Phase 3.2 HITL proposal-queue CLI surface.
|
||||
- S5: Promotion sweeps, capability-index entries, docs/memory
|
||||
housekeeping.
|
||||
|
||||
**Arc close:** all three tiers merged; Phase 5 trigger evaluated with
|
||||
evidence in hand; next arc scoped from it.
|
||||
|
||||
## 7. Non-goals (this arc)
|
||||
|
||||
- ADR-0246 §3.7 / identity-gate calibration (evidence-blocked).
|
||||
- `core/ports/` Ring-2/3 consumption (own future flag-gated units).
|
||||
- Universal-nested-in-connective, co-reference, tense bands.
|
||||
- Any serving-flag flip without explicit ratification.
|
||||
- Zig substrate work.
|
||||
125
docs/research/lane-drift-investigation-2026-07-24.md
Normal file
125
docs/research/lane-drift-investigation-2026-07-24.md
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
# Lane-Pin Drift Investigation — miner / curriculum / demo_composition
|
||||
|
||||
**Date:** 2026-07-24 · **Worktree:** `core-wt-gen` off main @ `5224b5e0`
|
||||
**Verdict:** two distinct causes; no determinism bug; one **real serving
|
||||
regression found and fixed** (register axis stripped from pipeline-served
|
||||
turns).
|
||||
|
||||
Context: during the Band v4-CM arc (2026-07-23), a `verify_lane_shas.py
|
||||
--update` dry run showed three lanes' pins stale. This investigation
|
||||
root-causes all three before any re-pin (generalization-arc Phase 0.1).
|
||||
|
||||
## Determinism check
|
||||
|
||||
Each drifted lane was run twice in a fresh worktree (fresh
|
||||
`uv sync --locked` venv, hermetic `CORE_ENGINE_STATE_DIR`): byte-identical
|
||||
SHAs across runs for `miner_loop_closure` (`537094fe…`) and
|
||||
`curriculum_loop_closure` (`cb94ca00…`). **Not a determinism bug.**
|
||||
|
||||
## Cause 1 — content-digest widening (miner + curriculum): benign, stale pins
|
||||
|
||||
Commit `5c69b741` (2026-07-17, ADR-0244 Phase 5a "§2.7 content-id
|
||||
semantic rigor") widened `core/contemplation/schema.py::_content_digest`
|
||||
from 16-hex truncation to the full 64-hex sha256 (birthday-collision
|
||||
rigor). Both lanes' reports embed `finding_id` / derived `proposal_id`
|
||||
values from that digest: the fresh `finding_id` is the 64-hex extension
|
||||
of the exact pinned 16-hex prefix (`4d6b8b5e1d46eb60` →
|
||||
`4d6b8b5e1d46eb60ea7a…`), and every downstream `proposal_id` cascades.
|
||||
The commit re-pinned nothing. **Intentional, ADR-tracked, benign → both
|
||||
pins re-pinned surgically in this branch** (single-line edits; never
|
||||
`--update`, which rewrites every lane and silently drops erroring
|
||||
lanes' pins).
|
||||
|
||||
## Cause 2 — demo_composition: a REAL serving regression (found via the drift)
|
||||
|
||||
The fresh lane report flipped `register-tour` `all_claims_supported`
|
||||
true→false (plus `composite_supported`, which is `audit ∧ register`).
|
||||
Isolation showed the register tour's two substantive-differentiation
|
||||
claims failing while the three invariance claims held: **terse/convivial
|
||||
registers no longer differed from neutral on pack-grounded definitions**
|
||||
— the entire register axis (ADR-0077 R6 substantive + ADR-0071 R4
|
||||
decoration) was missing from pipeline-served surfaces.
|
||||
|
||||
Mechanism (three commits, each individually defensible):
|
||||
|
||||
1. `ff1dcb25` (2026-05-20, #76) introduced `resolve_surface` with a
|
||||
`_base_runtime_surface` precedence of **canonical-first** — the
|
||||
trace-hash identity capture preferred over the runtime's served bytes.
|
||||
Dormant at first.
|
||||
2. `e0d1b475` (2026-07-20, #96 fail-closed linguistic governance) routed
|
||||
pipeline turns through the resolver with `canonical_surface` populated
|
||||
→ pipeline-served turns began serving the pre-R6 canonical. The
|
||||
runtime's own `chat()` path stayed correct; `warmed_session`
|
||||
telemetry-consistency went red (0.9444) because TurnEvent (correct
|
||||
bytes) disagreed with pipeline (stripped bytes).
|
||||
3. `5a343b49` (2026-07-22, T13) "fixed" the telemetry red by
|
||||
back-stamping the pipeline's (stripped) surface onto TurnEvent —
|
||||
making telemetry consistent with the *regressed* serving. The register
|
||||
tour's claims — the falsifiable contract for the register axis —
|
||||
then failed, which is exactly the byte drift the lane pin caught.
|
||||
|
||||
Why no gate caught it: the e2e tests that pin this behavior
|
||||
(`tests/test_register_substantive_consumption.py::test_e2e_terse_*`)
|
||||
are red on main but are not in the curated smoke suite; the lane-shas CI
|
||||
job is the only gate that runs the demo lanes, and its failures were
|
||||
conflated with the known `public_demo` env flake.
|
||||
|
||||
### Fix (this branch)
|
||||
|
||||
`core/cognition/surface_resolution.py` + `core/cognition/pipeline.py`:
|
||||
|
||||
- `_base_runtime_surface` precedence corrected to **response-first**
|
||||
(served bytes = post-guard, post-R6, post-R4), canonical demoted to
|
||||
last-resort fallback.
|
||||
- New `SurfaceResolution.hash_surface`: the truth-path
|
||||
(canonical-precedence) bytes, kept in lockstep through substrate
|
||||
overrides, folds, hedge prefix, logos-morph override, and the
|
||||
speculative marker. `compute_trace_hash` now folds `hash_surface`,
|
||||
so **trace_hash stays register-invariant (ADR-0069 inv C) while the
|
||||
served surface carries the register**. `_prior_surface` (correction
|
||||
binding) also stays on the truth path so register packs cannot perturb
|
||||
teaching.
|
||||
- Removed dead `FailureClass` import + `_assessment_residual` (repo-wide
|
||||
unreferenced).
|
||||
|
||||
Verification: register tour **6/6 claims** (both substantive-differs
|
||||
claims restored AND `all_trace_hashes_identical` held);
|
||||
`tests/test_surface_resolution.py` (updated precedence tests + 2 new
|
||||
fallback tests), `test_register_substantive_consumption.py` (previously
|
||||
red e2e tests now green), `test_grounded_open_hedge_arm.py`,
|
||||
`test_cognitive_turn_pipeline.py`, `test_warmed_session_lane.py` (T13
|
||||
consistency) — all green; smoke suite via pre-push gate.
|
||||
|
||||
### demo_composition pin disposition (measured)
|
||||
|
||||
After the fix, every claims-supported flag in the lane report matches
|
||||
the committed report again (`all_claims_supported_a/b` true,
|
||||
`composite_supported` true). The only residual delta vs the 2026-07-14
|
||||
pin is the two tours' *inner payload* `sha256` values — those payloads
|
||||
embed per-prompt `trace_hash` values whose format legitimately changed
|
||||
post-pin (`e7d116c9`, 2026-07-20: Phase-B graph-topology inclusion in
|
||||
`compute_trace_hash`). Semantics restored, embedded hash format
|
||||
evolved → re-pinned to `f0611a2c…` with this diff as the audit trail.
|
||||
|
||||
## public_demo (not one of the three, for completeness)
|
||||
|
||||
Unchanged: its known failure mode is the env-timeout flake
|
||||
(`all_claims_supported=False` under cold/slow runs), documented in
|
||||
memory and the lane-shas remediation text. Do not re-pin from a laptop
|
||||
run; adjudicate on the Act runner.
|
||||
|
||||
## Bonus latent break found while re-pinning
|
||||
|
||||
`scripts/generate_claims.py` raised on every run since the
|
||||
`deduction_serve_v1` pin landed (2026-07-23): the pin was added to
|
||||
`PINNED_SHAS` without the required `_LANE_ADR` entry, so the lane-shas
|
||||
CI's `generate_claims.py --check` step has been red since then. Fixed
|
||||
here (ADR-0256 row added; CLAIMS.md regenerated, check green).
|
||||
|
||||
## Follow-ups (assigned in the generalization-arc plan)
|
||||
|
||||
- Tier S: add `tests/test_register_substantive_consumption.py` e2e
|
||||
tests to the curated smoke suite so this axis can never silently
|
||||
regress again (small, mechanical).
|
||||
- Tier S: document the lane-shas CI expectation that a red lane job is
|
||||
triaged per-lane, never assumed to be the public_demo flake.
|
||||
|
|
@ -74,6 +74,10 @@ _LANE_ADR: dict[str, tuple[str, str]] = {
|
|||
"ADR-0206",
|
||||
"Propositional entailment scored against an independent truth-table oracle; dev+holdout+external 716/716 correct, wrong=0, refused=0",
|
||||
),
|
||||
"deduction_serve_v1": (
|
||||
"ADR-0256",
|
||||
"Flag-gated deduction serving decides real English/member/fused arguments end-to-end under earned SERVE licenses; wrong=0 across all splits",
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -43,11 +43,11 @@ LANE_TIMEOUT_S = int(os.environ.get("CORE_LANE_VERIFY_TIMEOUT_S", "900"))
|
|||
|
||||
PINNED_SHAS: dict[str, str] = {
|
||||
"reviewer_registry": "681a2aab5aa4ffd58cd837ce5673c8b2a9545b570117aec3c02726a12f6876e6",
|
||||
"miner_loop_closure": "9f071733abe7dcacf759f928548ce738fb639af3fd6e4c621a651b306d7e77ce",
|
||||
"curriculum_loop_closure": "b46d56b2d209172cc3ffaf3776dc8dcfe55093f13587c5cb67372be6dfa23e8d",
|
||||
"miner_loop_closure": "537094fe21d7e6cfbaf42bfc32b82d669fa9bb05a132d2bc93c72b3ceb7762a6",
|
||||
"curriculum_loop_closure": "cb94ca0042d78ec2624129ff6493d52e767b69feea32d2997b85d88f1c0883af",
|
||||
"domain_contract_validation": "98ace04e3f02bbc5a8ad655bb6593c3f1ee64cb67014f1122fe6c3c85f48d22f",
|
||||
"fabrication_control_summary": "01e1b6b711141f2b4a14551d7df3ea482d8d6dd7b364a25c509f4f8d08cda8a8",
|
||||
"demo_composition": "e2ba2314d8768459fb6a8db082a4bbcf4107b5161d869804a4b2a33c3724081a",
|
||||
"demo_composition": "f0611a2ce41721dd40767fc6a83a08470d3c7fd7fc8f1ae8ba003abf8a25ec97",
|
||||
"public_demo": "7d8ba0dbae9287cfe0bf15d231fa78a75abc627121c14900439293e01e1cc1d3",
|
||||
"math_teaching_corpus_v1": "eaf160d145da29f9050ede8d58bf111b0f651dd40aeae9201857d0b97e014dd4",
|
||||
"deductive_logic_v1": "97a230949016e38d5e3f37a69e4245b320575ee70e5af92ff7607f7b05f74b5f",
|
||||
|
|
|
|||
|
|
@ -32,7 +32,10 @@ def _open_assessment() -> ContractAssessment:
|
|||
)
|
||||
|
||||
|
||||
def test_runtime_canonical_surface_has_base_precedence() -> None:
|
||||
def test_runtime_response_surface_has_base_precedence() -> None:
|
||||
# response_surface is the served bytes (post register R6/R4 —
|
||||
# ADR-0077/ADR-0071); canonical is the trace-hash identity capture
|
||||
# and must never displace served bytes when a response exists.
|
||||
resolved = resolve_surface(
|
||||
canonical_surface="canonical",
|
||||
pre_decoration_surface="pre-decoration",
|
||||
|
|
@ -41,13 +44,39 @@ def test_runtime_canonical_surface_has_base_precedence() -> None:
|
|||
contract_assessment=_closed_assessment(),
|
||||
)
|
||||
|
||||
assert resolved.surface == "canonical"
|
||||
assert resolved.surface == "runtime"
|
||||
assert resolved.articulation_surface == "articulation"
|
||||
assert resolved.authority == "runtime_canonical"
|
||||
assert resolved.authority == "runtime"
|
||||
assert resolved.fold_sources == ()
|
||||
assert resolved.authoritative is True
|
||||
|
||||
|
||||
def test_canonical_surface_is_last_resort_fallback() -> None:
|
||||
resolved = resolve_surface(
|
||||
canonical_surface="canonical",
|
||||
pre_decoration_surface="",
|
||||
response_surface="",
|
||||
response_articulation_surface="articulation",
|
||||
contract_assessment=_closed_assessment(),
|
||||
)
|
||||
|
||||
assert resolved.surface == "canonical"
|
||||
assert resolved.authority == "runtime_canonical"
|
||||
|
||||
|
||||
def test_pre_decoration_outranks_canonical_when_response_absent() -> None:
|
||||
resolved = resolve_surface(
|
||||
canonical_surface="canonical",
|
||||
pre_decoration_surface="pre-decoration",
|
||||
response_surface="",
|
||||
response_articulation_surface="articulation",
|
||||
contract_assessment=_closed_assessment(),
|
||||
)
|
||||
|
||||
assert resolved.surface == "pre-decoration"
|
||||
assert resolved.authority == "runtime_pre_decoration"
|
||||
|
||||
|
||||
def test_useful_realizer_requires_conjugate_coherence() -> None:
|
||||
"""Realizer shim only when conjugate geometric contract is closed."""
|
||||
resolved = resolve_surface(
|
||||
|
|
|
|||
Loading…
Reference in a new issue