feat(optimize): kind: optimization — factorial/response-surface campaigns - #302
feat(optimize): kind: optimization — factorial/response-surface campaigns#302sriumcp wants to merge 148 commits into
Conversation
…s-Research#289) Reflects the reflective branch merge (AI-native-Systems-Research#279): SDK dispatcher, meta-findings, lineage, observability, statistical modules, and operational hardening. Co-authored-by: Mert Toslali <toslali@ibm.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…I-native-Systems-Research#290) * docs: add design spec for style-customized visualization Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: add implementation plan for style-customized visualization Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(viz): add --summary-md flag for summary override Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(viz): add style customization to /visualize-campaign skill Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(viz): pass style intent through /post-campaign to visualization Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(viz): address PR review issues for style-customized visualization - Add insights.json assembly step: combine restyled dead-ends, frontiers, interactions into bundled format expected by --insights flag - Fix design spec: remove unimplemented --dead-ends/--frontiers/--interactions flags, document that --insights handles the bundled format - Fix :: delimiter parsing: specify "split on FIRST :: only", handle empty style after ::, document campaign names must not contain :: - Add temp directory lifecycle: rm -rf before writing to prevent stale files - Add all-fail behavior: ask user instead of silently showing canonical - Add error handling for --summary-md: existence check with clear error message - Restore blank line between code sections in visualize_campaign.py - Clarify "parallel" means independent tool calls in a single response - Fix architecture diagram to show actual 5-call split Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: remove plan and spec docs from feature branch These were working documents used during development and are not needed in the final merge. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(wiki): document style intent syntax for visualize-campaign Add `:: <style>` delimiter documentation for /visualize-campaign and /post-campaign skills, including style examples table and behavior description for styled vs unstyled rendering. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…esearch#296) (AI-native-Systems-Research#297) The runtime reads campaign.channels at every DESIGN/FINDINGS gate (orchestrator/iteration.py -> orchestrator/channels.py) and POSTs a markdown gate summary to each configured channel, but campaign.schema.yaml never declared the property while setting additionalProperties: false at the top level. So `nous run` aborted at pre-flight with: Campaign validation error: Additional properties are not allowed ('channels' was unexpected) making the documented channels: feature impossible to use — the campaign is rejected before the run starts. Add a channels property whose item shape mirrors exactly what orchestrator/channels.py reads: kind in {webhook, slack} (optional; defaults to webhook), url (webhook), webhook_url (slack), and optional headers (webhook). additionalProperties: false on each entry catches field typos, matching the strictness of the rest of the schema. Add regression tests in tests/test_schemas.py (accept / default-kind / invalid-kind / unknown-field) so the schema and runtime can't drift apart again. Fixes AI-native-Systems-Research#296 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Adds a factorial/response-surface campaign kind so optimization problems
are solved by estimating a response surface rather than by sequential
one-factor-at-a-time search.
Motivation is the composition barrier documented in "Discovery Is Easy,
Composition Is Hard" (2026-08-04): on the Certus cold-read path, five of
seven LLM optimizers stalled near half the available headroom because
batching was -9.5% in isolation but required for the winning compound.
Any search that prunes regressions cannot reach that optimum. Factorial
designs have no composition barrier because they estimate interaction
terms directly instead of composing increments.
Design was revised against the local campaign corpus, which is largely
optimization-shaped already:
* alert-threshold-robustness hand-rolls a 1350-cell grid that a
resolution-V design covers in ~50 runs -> multi-level factors
* ordering-theorem's headline finding IS an interaction (7.3x) ->
resolution V by default, categorical mechanism factors
* composite-sensitivity-boundary trades per-regime -> constrained
multi-regime response, not a scalar objective
* holdout-selection separates a held-out key -> mechanical leakage
refusal
Token cost drops by moving the per-configuration decision out of the
model: ~3 substantive LLM calls per campaign against 60-90 benchmark
runs. findings/principles are projected deterministically from fitted
effects, following the pure-Python meta_findings pattern (AI-native-Systems-Research#155), so the
durable artifact set is preserved without per-iteration prose.
Correctness is bought with two mandatory check families per factor:
manipulation predicates (did the lever engage?) and native-idiom
property/metamorphic tests committed to the target repo (is the
mechanism correct?). Relations are classified correctness (hard-fail)
vs behavioral (recorded finding) so a genuine non-monotonicity is a
discovery, not a crash.
Structure is a parallel orchestrator/optimize/ subpackage with one
delegation point, leaving the reflective path untouched. Scopes the
AI-native-Systems-Research#159 tier ladder to the reflective kind, with the justification that a
pre-registered matrix strengthens rather than weakens the
anti-p-hacking property it protects.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Cleans up the campaign spec for end-user (and AI-author) ergonomics
after reviewing how the corpus actually declares knobs.
Factor types collapse from continuous|ordinal|categorical to
numeric|choice. The retired vocabulary asked the author a statistics
question; the new one asks a domain question they can answer directly --
"are values between these levels runnable?". decay_guard ∈
{off,0.004,...} makes the distinction concrete: 'off' sits in a list of
numbers, so it is a choice, and no author has to reason about how a
fitter would interpolate to find that out.
Adds `grid` to make the reported optimum runnable: refinement fits
continuously, then snaps the stationary point to the grid step, so
confirm always runs a configuration that exists and validates the
snapped point rather than the theoretical one. This closes the open
question about ordinal refinement -- restricting to declared levels
would forfeit interior optima, and plain rounding can report a config
the target cannot run.
Replaces the manipulation `assert: "== {value}"` mini-DSL with the same
{observable, op, value} shape already used by constraints and regimes,
so there is one comparison vocabulary in the spec and no bespoke parser.
Adds when/when_not guards, since a check is often meaningless at one
level. `{level}` is now the only interpolation token anywhere.
`apply` accepts a bare string as CLI-flag shorthand; screen_levels
defaults to first/last so the common two-level case needs neither.
Adds §11, a full worked example restating alert-threshold-robustness
(the corpus campaign that hand-rolled a 1350-cell grid) as ~40 runs.
Verified both YAML examples parse and self-conform to the spec's own
rules -- which caught severity_boundary declaring only a behavioral relation,
i.e. an example that would have failed its own validator.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Campaign authors need to supply domain knowledge beyond knobs -- "the connector must be P2P", "the workload is single-tier", "explore shortest-job-first". Today that lives in target_system.description, which the spec inherits unchanged and which remains right for narrative framing. But prose alone cannot carry it in this kind: screen and refine make zero model calls, so a directive that exists only as prose is unenforceable during exactly the two stages that spend the benchmark budget. That is the AI-native-Systems-Research#221 failure mode -- honored by the phase that reads prompts, ignored by the phase that does the work. So author intent splits by who must act on it: design_space.invariants -- properties Python checks on EVERY config, in every stage, using the same {observable, op, value} vocabulary as constraints and manipulation. Rejected pre-run when statically determinable from the matrix row, hard-failed post-run when only observable afterwards. Distinct from locked_parameters (which pins an input) because "the connector is P2P" is a property of the resulting system, possibly emergent from several settings -- asserting the observed transfer path is stronger than pinning the flags believed to produce it. Distinct from response.constraints, where a violation means "this config is infeasible, keep exploring" rather than "the campaign left its design space". guidance -- two named prose slots, factor_nomination (read at verify) and interpretation (read at confirm), separated because blending them wastes tokens in both. The authoring rule: anything you would be upset to find violated after 60 runs is an invariant, not guidance. The worked example gains a lookahead-bias tripwire as I2, which is the case that makes the argument concrete -- "don't peek past the OOS boundary" reads as obviously-satisfied in prose and renders every downstream number meaningless while still looking excellent. Adds the invariant-violation row to the failure taxonomy, the prose-instead-of-invariant anti-pattern to the guide outline, and re-verifies both YAML examples parse and self-conform. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
13 tasks, 63 TDD steps, from the approved design spec. Full test bodies and
implementations for the four statistically load-bearing tasks (factors,
predicates, design generation, effect fitting); precise assertion lists for
the mechanical ones (matrix, relations, stage rule, artifacts, runner,
schema, wiring, regression, guide).
Every numeric assertion in the plan was executed and verified before being
written down, not assumed:
* resolution V on 5 factors is 16 runs with generator E=ABCD and zero
aliasing; resolution III on 7 factors is 8 runs with 21 two-factor
interactions confounded onto mains (Box-Hunter-Hunter)
* the closed form contrast/N equals numpy lstsq to machine precision and
recovers planted coefficients, including the L5 sign flip (main -0.95,
interaction +1.60) that defeated five of seven optimizers in the study
* pure-error variance 0.00092 on df=4; rotatable alpha sqrt(2) for k=2;
grid-snapped midpoints 9.0 and 0.85
* float error is ~8.9e-16, so == is flaky and every assertion uses
math.isclose
Self-review caught one dropped requirement: integrity_command was
declarable in the schema but nothing executed it, leaving one of the
spec's three guardrails inert. Task 9 now runs it per config through an
injected seam and treats a non-zero exit as rejected rather than
infeasible -- corrupt output is not evidence about the design space.
No new harness dependencies: orchestrator/optimize/ uses stdlib arithmetic
plus the already-declared scipy.stats. numpy stays out (it is currently
unused across orchestrator/), and hypothesis/rapid/proptest belong to
target repos, not to Nous.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Factors declare type: numeric | choice — a domain question ("are values
between the levels runnable?") rather than a statistics one. grid snaps a
fitted optimum to a runnable step so confirm never tries to run K=4.7.
Rejects the retired continuous/ordinal/categorical vocabulary, factors
without a manipulation check, and factors whose only relation is
behavioral (nothing would then catch a broken lever).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two Global Constraints were wrong in ways that would mislead every implementer: pytest is not installed in the project venv, so `.venv/bin/pytest` (28 references) does not exist. The working invocation is the system binary at /opt/homebrew/bin/pytest; `.venv/bin/python` remains correct for running Python directly. Discovered while independently verifying Task 1's test claims -- the risk is an implementer reporting "tests pass" after running nothing. numpy IS importable inside the venv, pulled in transitively by scipy. The no-new-dependencies rule therefore cannot be enforced by "it would fail to import" and must be checked statically. The check must use AST import extraction rather than grep: Task 1's optimize/__init__.py docstring names all five forbidden libraries as a warning to future authors while importing none of them, so grep reports a false positive. Records the expected baseline (1374 passed, 1 skipped) so a regression is visible rather than inferred. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
manipulation, response.constraints, response.regimes and
design_space.invariants share one {observable, op, value} shape, so there
is no bespoke assertion mini-language to learn or mis-type. when /
when_not guard which levels a check applies to.
is_trivial() flags predicates that cannot fail (> 0, != null): a lazy
check makes a broken lever look verified, which is worse than no check.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…etection Fix round 1 of code review findings: I1: Add validation to reject predicates with both when and when_not guards, as they are complementary level guards. The other three check families (design_space.invariants, response.constraints, response.regimes) never go through parse_factors, so this module is the single place that can catch this error for all four check families. I2: Add missing: bool field to Verdict to distinguish an absent observable (telemetry never emitted) from a genuine comparison failure (telemetry emitted but has wrong value). Task 9's runner needs this distinction; adding it now prevents retrofit after callers depend on detail strings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fractional designs use published generators (Box-Hunter-Hunter; Montgomery Table 8.14) so the alias structure matches the textbook rather than whatever the code happens to compute. Verified: 5 factors at resolution V is 16 runs with E=ABCD and zero aliasing; 7 factors at resolution III is 8 runs with 21 two-factor interactions confounded onto main effects. alias_pairs() returning [] is the resolution-V property -- every main effect and 2-factor interaction separately estimable. That matters because ordering-theorem's headline finding IS an interaction (preemption + FIFO = 7.3x worse), which a main-effects-only screen inverts. Center points are what make a lack-of-fit test possible; without them a campaign cannot say whether its own model form is adequate. Fixed two bugs relative to the plan's reference snippet, both caught by its own tests: (1) fractional_factorial's "unachievable resolution" error branch was dead code because min_runs_for's fallback made the guard condition always true; (2) alias_pairs double-counted resolution-III aliasing (42 instead of 21) by also reporting 2fi-2fi matches that were purely transitive echoes of a shared main-effect alias already reported. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Audited every _GENERATORS entry against its claimed resolution rather than only the two the plan's oracles cover. The (7,5) and (8,5) entries were labelled resolution V but are actually resolution IV: 3 and 9 two-factor interactions respectively are confounded with each other. This is the worst of the three plan bugs found so far because resolution V is the feature's default and its entire justification. A campaign screening 7 factors would be told it has unaliased two-factor-interaction estimates while 3 pairs are confounded, and the fit would report tight confidence intervals on the aliased terms -- nothing downstream can detect that. Corrected to the verified generators: 7 factors res V is n_base=6 with G=ABCD (64 runs); 8 factors res V is n_base=6 with G=ABCD, H=ABEF (64 runs). Both re-audited as balanced, mains mutually orthogonal, zero 2fi on mains, zero 2fi on 2fi. These match the literature -- 2^(7-1) and 2^(8-2). The plan's claim of 32 runs for either was wrong; 32 runs cannot carry resolution V at those factor counts. The run-count increase is the true price of unaliased 2fi estimation, not a regression. Task 10's max_runs rule is what surfaces that cost honestly to a campaign author. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Auditing every _GENERATORS entry (not just the two spot-checked oracles) found that (7,5) and (8,5) claimed resolution V at 32 runs but actually had 2fi-on-2fi aliasing (3 and 9 pairs respectively) -- genuine resolution IV mislabeled as V. A campaign screening 7-8 factors would have been told its two-factor interactions were separately estimable when several were confounded, with tight confidence intervals on the aliased terms hiding the problem completely. Corrected to the literature values: 7 factors res V is G=ABCD at 64 runs (2^(7-1)); 8 factors res V is G=ABCD, H=ABEF at 64 runs (2^(8-2)). Both independently verified: balanced columns, mutually orthogonal main effects, zero 2fi-on-main and zero 2fi-on-2fi aliasing. Added a parametrized audit test that regenerates the alias structure for every _GENERATORS entry from first principles and checks it against its claimed resolution, so a future entry is audited automatically rather than trusted on a comment. The two original oracles (5 factors res V = 16 runs/0 aliases; 7 factors res III = 8 runs/21 aliases) are unchanged and still pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…licit min_runs_for returns 2**k for any (k, resolution) not in _GENERATORS. That value is a conservative upper bound, not the true minimum -- a smaller fractional design may exist but isn't tabulated here. The prior docstring didn't say this, inviting a caller (e.g. a future run-budget feasibility check) to treat the fallback as an authoritative minimum and reject a campaign that a real design could satisfy far more cheaply. Docstring now states the contract plainly and tells callers to check `(k, resolution) in _GENERATORS` before trusting the value as a minimum. No behavior change. Added test_min_runs_for_is_exact_only_for_tabulated_cells to pin the fallback's documented semantics (exact for tabulated cells, 2**k upper bound otherwise) so it doesn't drift silently. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both failed against a correct implementation, so they were plan defects rather than implementation bugs. Diagnosed from the failure output and verified fixed before writing. test_aliases_are_carried_onto_the_fit_as_a_caveat called fit_effects with the default include_interactions=True on a saturated resolution-III 7-factor design. That design has 8 runs; the requested model has 29 terms (intercept + 7 mains + 21 two-factor interactions), which is not estimable, so fit_effects correctly raised "design matrix is singular". The property under test -- that aliasing propagates onto the Fit as a caveat -- needs no interaction terms at all. Now passes include_interactions=False and verifies 21 alias pairs carry through. The singular-matrix guard stays as is; refusing an unestimable model is correct and valuable. test_strong_curvature_is_detected_as_lack_of_fit set all four center points to the identical value 14.0. Identical replicates give pure_error_var == 0, which trips the implementation's own `pe_var > 0` guard, skips the F test, and leaves lack_of_fit_p as None -- exactly what the test then asserted against. The center values now carry small distinct perturbations, so pure error is real and the lack-of-fit F test fires as intended (verified: pe_var 2.9e-04, F 1.1e+05, p 6.1e-08). The `pe_var > 0` guard must not be relaxed: zero pure-error variance means no independent error estimate, and dividing by it would yield an infinite F statistic. Reporting that as significance would be fabricating a result. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
For orthogonal +/-1 designs the OLS coefficient has the exact closed form contrast/N, verified equal to numpy lstsq to machine precision -- so the arithmetic stays auditable and numpy stays out of the harness. Non-orthogonal (central-composite) fits use a direct solve of the normal equations; the systems are tiny. The load-bearing test plants the L5 sign flip -- main effect -0.95, interaction +1.60 -- and asserts the fitter recovers both. That is exactly the landscape that defeated five of seven optimizers in the motivating study: a factor harmful alone and required for the winning compound. Significance is left as None when there are no replicated center points, rather than guessed. An unknown effect is not a null effect, and a fabricated interval is worse than an absent one. Two brief-provided tests were defective and were fixed rather than the implementation weakened: - test_aliases_are_carried_onto_the_fit_as_a_caveat requested 29 terms (1 + 7 mains + 21 2fi) from an 8-run resolution-III design; the singular matrix ValueError was correct, so the test now passes include_interactions=False, since the property under test (aliases carried forward) doesn't need interactions fit at all. - test_strong_curvature_is_detected_as_lack_of_fit set all four center replicates to the identical value 14.0, giving pure_error_var == 0.0 and correctly disabling the F test (dividing by zero pure error would fabricate significance). Center values now carry small distinct perturbations around 14.0 so pure error is non-zero and the lack-of-fit F test fires as intended. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…king
expand() turns a coded design row into a runnable config via each factor's
apply spec -- the seam that removes the LLM from the inner loop. {level} is
the only interpolation token.
Run order is randomized from a recorded seed, so time-ordered drift
(thermal, cache warming) cannot masquerade as a factor effect while the
campaign stays reproducible.
check_fidelity reports three violation classes -- level drift, missing
planned row, unplanned extra row. All three are hard failures: a silently
skipped cell changes the design's actual resolution, so tolerating it would
let the campaign overstate what it can estimate.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Task 5's assertion 8 named `aliases` as a design_matrix.json field but never said to populate it from design.alias_pairs(), so a faithful implementation left it empty. Verified: a resolution-III 7-factor design has 21 alias pairs per alias_pairs(), while its payload recorded aliases: []. design_matrix.json is the pre-registered artifact a human or AI reads to judge what a screen can actually estimate. An empty list on a heavily confounded design does not merely omit information -- it asserts the opposite of the truth, which is the class of defect this feature exists to prevent. Now requires both directions be asserted: res III carries a non-empty list matching alias_pairs(), and res V carries an empty one, so the resolution-V property is visible in the artifact rather than merely absent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nous checks a contract, never test code: each relation names a native_test, the declared test_command runs, and the identifier must appear in the results having passed. hypothesis / rapid / proptest / RapidCheck are the target repo's business, so the harness needs zero language knowledge. A relation declared but absent from the results is a FAILURE, not a pass -- otherwise a typo'd identifier silently disables a correctness gate. classify_failures keeps behavioral violations out of the correctness bucket: a monotonicity break is a discovery (L5 was -9.5% alone and required for the winning compound), and hard-failing on it would make the campaign blind to what it exists to find. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
matrix_payload's aliases key was always []. A resolution-III design recorded aliases: [] even with 21 confounded pairs -- the pre-registered design_matrix.json artifact silently asserted the opposite of the truth. Populate it from design.alias_pairs(design), which already returns a sorted list, so the payload stays deterministic across calls. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
fit_effects computed one se = sqrt(pe_var / n) shared across every term, where n was the total row count including center (and, on a central composite, axial) points. Those points contribute 0 to a +/-1 coded column's sum of squares, so they inflate n without adding information about that term -- systematically understating every two-level term's SE and CI half-width whenever non-corner points are present. On the resolution-V 5-factor + 5-center design (16 corners, 21 rows) this understated CI half-widths by sqrt(21/16) ~= 1.1456 (about 14.6%), enough to flip real decisions: effect sizes 0.0190-0.0205 read as significant under the buggy scalar SE but correctly read as not-significant once SE is computed from each term's own column. Verified by temporarily reverting to the scalar form and confirming the new regression test fails with significant=True (false positive) before the fix and passes after. Fix: se_j = sqrt(pe_var / sum_i x_ij^2), using that term's own column (cols[idx]) rather than a shared n. This also generalizes correctly to central-composite fits, where axial points contribute a different sum of squares per column, so no single scalar could ever be right for all terms there either. The pe_var > 0 guard, None significance with no center points, and the L5 sign-flip oracle (main -0.95, interaction +1.60) are unchanged and still pass exactly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The reference fit_effects used one scalar se = sqrt(pe_var / n_total) for every term. Centre points contribute 0 to a +/-1 coded column, so they inflate n without informing a main effect: sum(x^2) is 4 for A/B/AB but 8 for the intercept on a 2-factor + 4-centre design, understating main-effect SEs by sqrt(2). On res-V 5-factor + 5 centres it is 14.6% too narrow. This was the most dangerous defect of the run because it changes decisions, not just numbers. Verified: at true effect sizes 0.0190-0.0205 on that design the buggy formula reports significant=True where the correct SE gives False -- a false positive. dropped_factors keys off significant and the stage rule keys off dropped_factors, so a campaign would spend refinement budget on noise and fit an optimum over knobs that do nothing. All 15 tests passed regardless, because no planted effect sat near the boundary. Now computes se_j = sqrt(pe_var / sum_i x_ij^2) per term. Also documents the limitation found while validating the fix against an explicit (X'X)^-1 inverse: that formula is exact only where a column is orthogonal to all others, which holds for main effects and 2-factor interactions on every design this module generates, but NOT for the intercept or pure-quadratic terms on a central composite (exact 0.4208 vs 0.2887 on a 2-factor CCD). Quadratic CIs are therefore optimistic; they describe surface curvature and are never used as a significance gate, and solve_stationary_point consumes the estimates rather than their intervals. Adds the boundary test whose absence hid this, asserting the SE matches the corner count and that a 0.02 effect inside the noise floor is not reported significant. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The per-term se_j = sqrt(pe_var / sum_i x_ij^2) formula equals the exact sigma * sqrt((X^T X)^-1_jj) only when that term's column is orthogonal to every other column in the model. That holds for every main effect and two-factor interaction on every design this module generates -- the terms dropped_factors and the stage rule actually gate on -- so those CIs are exact. It does not hold for the intercept or the pure-quadratic terms on a central composite, whose columns are mutually correlated: measured against an explicit (X^T X)^-1 inverse on a 2-factor CCD, A^2/B^2 exact SE is 0.420813 vs. 0.288675 from the per-column formula, about 1.46x too narrow. Documented rather than fixed with a full matrix inverse: quadratic terms describe surface curvature and are consumed by solve_stationary_point as point estimates only, never gated on their CIs. No behavior change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Between-stage adaptation is arithmetic on effect sizes, not a model call: drop factors whose CI contains zero, refine when a multi-level numeric factor survives, otherwise go straight to confirm. Iteration N+1 inherits estimates and intervals rather than prose, which is a stronger form of "use what N learned" than principle-passing alone. Four triggers name the cases where Python cannot decide and the model should be re-consulted: every factor within noise (wrong factor set), significant lack of fit (wrong model form), stationary point outside the declared hull (ranges too narrow), and a behavioral relation violation (possible real non-monotonicity worth interpreting). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Task 7's interface list gave decide_after_screen(fit, factors, *, alpha) and
decide_after_refine(fit, factors, stationary) -- neither accepting relation
verdicts. So Trigger.BEHAVIORAL_VIOLATION was defined but impossible to set:
verified that it appears only in stage.py's docstring, and that
classify_failures (which produces behavioral failures) has no call site
there. A required escalation signal was dead code.
It is required. The design spec lists it as escalation trigger 4 in section
6.3 ("a behavioral relation violated -> possible real non-monotonicity worth
interpreting") and names the same case in the section 6.4 failure table.
Both functions now take behavioral_failures as an optional tuple defaulting
to empty, so existing call sites and the screen stage (which can run before
any native test run) are unaffected. stage.py stays a pure function of its
inputs -- the caller runs the target's tests and classifies verdicts; this
module only reports that the signal arrived.
Adds three assertions, including the one that matters most: a
behavioral-only trigger must still ADVANCE the stage. A monotonicity break
is a discovery -- the motivating case is a lever measured -9.5% alone yet
required for the winning compound -- so halting on it would make the
campaign blind to exactly what it exists to find.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
BEHAVIORAL_VIOLATION was a dead enum member: nothing could set it, since neither decide_after_screen nor decide_after_refine had an input carrying behavioral relation verdicts (relations.classify_failures's second bucket). The design spec's escalation trigger 4 requires it. Add an optional behavioral_failures: tuple[RelationVerdict, ...] = () keyword to both functions. A non-empty tuple raises BEHAVIORAL_VIOLATION and names the relation id(s) in the rationale, but never blocks stage advancement -- a behavioral violation is a discovery worth interpreting, not a reason to stop (the motivating case: a lever measured -9.5% alone yet required for the winning combination). The empty default keeps every existing call site and test unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ction Four new schemas (design_matrix, runs_row, effects, relations) plus writers using atomic_write. runs.jsonl is append-only so a crash leaves completed rows intact. project_findings() is what makes "zero LLM calls at screen and refine" true without dropping the durable artifact set: a fitted effect with a confidence interval already contains a claim, a direction, a magnitude and quantitative evidence, so restating it in prose would cost tokens and add nothing. Pure Python, following the meta_findings (AI-native-Systems-Research#155) precedent, and it validates against the UNCHANGED findings schema -- so /post-campaign, index-wiki, visualize-campaign and the registry keep working untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The append-only requirement said "a crashed run must leave the completed rows intact" -- satisfied by never rewriting the file, but insufficient. Verified: append 3 complete rows, then a torn final line simulating a crash mid-write, and read_runs raises JSONDecodeError, so all three completed rows become unreadable. They are preserved on disk and unusable, which operationally loses them. That defeats the guarantee append-only exists to provide. The realistic scenario is a campaign dying partway through a 60-run sweep; it should refit on whatever completed and honestly report the reduced resolution -- the degrade-the-claim-not-the-data behaviour the spec asks for -- instead of failing to read its own results. read_runs must now skip a malformed TRAILING line (the only line a crash can tear) and report the skip. A malformed line in the middle must still raise: that is not a crash signature, and tolerating it would hide real corruption rather than recover from an interrupted write. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
append_run writes runs.jsonl one line at a time, so the only way a line can be malformed is a crash mid-write of the LAST line -- every earlier line was already flushed by a prior, completed append_run call. Before this fix, read_runs raised JSONDecodeError on that torn line, which made every already-completed row unreadable -- operationally indistinguishable from losing them, defeating the entire reason runs.jsonl is append-only (a crashed run must leave completed rows intact and refittable). read_runs now skips a malformed line only when it is the last non-blank line, logs a warning naming the file/line number so the skip is visible rather than silently swallowed, and still raises on a malformed interior line -- a crash cannot tear an interior line, so that failure mode is real corruption, not a crash signature, and must not be tolerated. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Three commits pushed from a field test of this kind against a real target (BLIS /
|
…he slowest corner
The original §7.1 advice ("measure the two extreme corners, take the larger,
double it") is incomplete for a compound objective, and the gap cost a row in a
real campaign even after the ceiling had already been raised once from a corner
measurement.
When the objective is a search — a bisection over arrival rate, a ramp, a
convergence loop — each probe's cost depends on the VALUE being probed, not only
on the configuration. A capacity search at rate r over horizon T simulates r x T
work, so a probe at 24 req/s costs ~16x one at 1.5 req/s. The bracket's reachable
ceiling therefore sets the worst-case row:
worst row ~= (max reachable probe / baseline probe) x baseline row cost
with max reachable probe = hi x 2^(expansion steps) for a doubling search. A
bracket of [0.75, 3.0] over 5 evaluations reaches 24 — 16x baseline — so a 330 s
baseline row implies a ~5,300 s worst row. A 2,400 s ceiling looks generous
against the measured corners and is still 2x short.
The perverse consequence is what makes this a trap rather than an oversight: a
BETTER configuration costs MORE to measure, because the search must climb higher
before it finds a failing point. The rows most likely to time out are therefore
the ones carrying the best candidates, and losing them biases the fitted surface
toward mediocre configurations while every diagnostic still looks healthy. The
guidance is now to bound the search explicitly — cap the bracket or the probe
value — rather than letting the timeout do it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… not a speed knob
Adds a measured lesson and de-simulator-ifies the whole of §7. Nous is not
tied to any one target, so the section now speaks in target-agnostic terms
("load level", "observation window", "an extreme tail statistic") while keeping
the concrete numbers that make each lesson credible.
New §7.6. Many objectives are computed over a window of observed behaviour — a
trend, a rate, a steady-state level. Such a metric has a SETTLING requirement:
the window must be long enough that start-up behaviour stops dominating it. Trim
it for speed and you do not get a noisier version of the same measurement, you
get a different and biased one.
Measured, same configuration and load and seeds:
short window (2/3 length): 38-44 s/run, ~2540 samples, trend +0.1643 / +0.0625
-> verdict GROWING
full window: 70-74 s/run, ~3875 samples, trend +0.0553 / -0.0247
-> verdict SUSTAINED
Opposite verdicts, and the short window is 1.8x faster — which is exactly what
makes it tempting. The short window still contains the cold-start ramp, and a
trend fitted across a ramp reads as growth.
The bias is not symmetric: a too-short window declares saturation early on EVERY
configuration, so every reported optimum shifts the same way. A 1.8x speedup that
moves every number one direction is not a speedup, it is a different experiment.
Validation recipe: measure a configuration you believe is comfortably inside
capacity at your candidate window and at 1.5x it. If the verdict or fitted value
changes, the metric has not settled. Lock the longer window into
locked_parameters so a later iteration cannot quietly trim it.
Also notes this is the same failure mode as computing the statistic over the
wrong REGION rather than the wrong LENGTH — in the same campaign a trend computed
over a window including the post-arrival drain reported "stable" for a system
whose backlog grew throughout the active phase.
§7.1 reworded away from a specific target's flag names, and its worked example
now says plainly that the 2400 s ceiling it describes killed two rows.
Checklist gains the window check and renames row 7 to worst-PROBE timing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…onfirm blocks `kind: optimization` executed its design strictly sequentially with no way for a campaign to request concurrency: `execute_design` was a plain `for row in rows:` loop, there were no concurrency primitives anywhere in `orchestrator/optimize/`, and no schema field. Measured on a real campaign, an 18-row screen where rows take 5-40 minutes is several hours of wall clock on a mostly-idle machine. This adds concurrency on the ONE axis where it costs the methodology nothing, and structurally forbids it on the axis where it would not. WHY NOT SIMPLY PARALLELIZE THE SCREEN. `stage_runner.py`'s run-order comment explains that randomized run order exists to protect a factorial design against drift confounding — a warming cache, a throttling machine, a background job. Co-scheduled rows contend, so a row's response would depend on which other rows ran beside it. For an objective that measures where a system saturates, that is first-order. `resolve_max_parallel(opt, *, stage_name)` therefore returns 1 for every stage except `confirm`, so screen/foldover/refine cannot opt in even by declaring the field. Verified: a campaign declaring 4 resolves to 4 at confirm and 1 at screen. WHY CONFIRM IS DIFFERENT. `_confirm_rows` emits one complete replicate block at a time, with the run-order shuffle INSIDE a block. Within one block each finalist is measured exactly once, so contention is symmetric across precisely the things being compared — which is what makes it harmless. Blocks are therefore a BARRIER: rows are grouped by `ConfigRow.replicate` in first-appearance order (never sorted, so the in-block shuffle survives) and each group drains before the next is submitted. A pool kept full across the boundary would overlap one finalist's replicate 2 with another's replicate 3 and reintroduce the asymmetric-neighbour confound invisibly. POSITIONAL ORDER, which is correctness-critical. `_finish_confirm` appends measurements in row order and `terminal_regret_bound` zips them, so position i must be replicate i for every finalist; a reordering would silently mispair the paired-differences bound rather than fail. Guaranteed structurally: outcomes are written into a pre-sized `results[pos]` slot per INPUT position, never appended from a completing worker, with a defensive abort if any slot is unfilled. The test scripts per-row delays so completion order is the exact reverse of submission, and asserts the reversal really happened (otherwise it proves nothing), that indices come back [0..5], AND that response values land against the right indices — so an index-only fixup still fails. `parallel_arms.run_units` was NOT reused. Its `max_parallel` is validated and then ignored (the body is a synchronous append loop, so its "same order as units" promise holds only because nothing is concurrent), it is typed to `ArmUnit`/`ArmUnitResult` rather than `ConfigRow`/`RunOutcome`, and it has no notion of a replicate block — which is the entire statistical basis for this bound. Axis C (across arms) remains its job. Threads rather than processes: every unit of work shells out and waits, so the GIL is held only to spawn and parse, and processes would break the injected-callable seam a test's in-process fake runner depends on. The measurement path was already race-safe — `config_patch` materializes a per-run copy of every patched file. `design_matrix.json` records the EFFECTIVE value beside `run_order_seed`, so a screen matrix reads 1 even under a campaign declaring 4. A pre-registration that claimed randomized run order while executing concurrently would assert a guarantee it did not provide. Rule 19 WARNS when the value exceeds the validating machine's CPU count, naming the count it compared against since nothing in the campaign declares target cores. The guide gives prominent placement to the axis this does NOT implement: the biggest wall-clock win is usually independent probes INSIDE one objective evaluation (target-side, in `run_command`), which speeds up every stage and introduces no cross-row confound. `max_parallel` only helps confirm. Invariants: no if/elif decides a next stage (the one `stage_name` comparison returns an integer bound), and no model or dispatcher call is added anywhere. Tests: 23 new behavioral tests. Full suite 2108 passed, 1 skipped (from 2085). Known pre-existing defect found and deliberately NOT fixed here: a confirm round's `design_matrix.json` has never validated against its schema on any revision — it carries `finalists`, `round`, and `kind: "shortlist_replicate"`, none declared. Verified against a clean tree. Worth its own change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`design_matrix.json` IS the pre-registration record — the artifact attesting what
was registered before any result was seen. On every prior revision, a `confirm`
round's matrix failed its own schema, so that attestation could not be relied on.
An audit (not a three-field patch — the same drift class was already found once on
this file) turned up NINE undeclared things, including two whole capabilities the
schema never permitted:
round -> integer, minimum 1 (the registered {"round": {">=": n}} guard reads it)
finalists -> array, minItems 1, closed items requiring key/levels/why
kind "shortlist_replicate" -> added to enum
kind "foldover" -> added to enum: an ENTIRE design family the schema rejected
folded_on -> ["string","null"]; null means full foldover, not a missing value
screen_iteration -> integer, minimum 1
alias_consequential-> array of 2-item string pairs, mirroring `aliases`
role "confirm" -> added to the row role enum
apply.finalist -> integer, minimum 0
`design.combine`'s `kind: "combined"` is deliberately EXCLUDED: it is produced at
fit time over two already-registered blocks and never reaches disk, so declaring
it would describe an artifact no stage writes. `build.py`'s `role="baseline"` rows
likewise — negative row_index, outside design-matrix bookkeeping.
The schema was NOT weakened to make this pass. `additionalProperties: false`
stands, and because `round`/`finalists` cannot be globally required (screen,
foldover and refine write neither) while a `shortlist_replicate` matrix without
them is not a pre-registration of terminal discrimination at all, one readable
`allOf` conditional requires them for that kind only. Verified in all four
directions: fires on a confirm matrix missing them, passes with them, screen
matrices stay valid, a brand-new key is still rejected.
ROOT CAUSE, worth naming because it is why this survived: the existing test
validated a freshly-built `matrix_payload` SKELETON, but every stage ENRICHES that
payload before `write_design_matrix` sees it. Validating the skeleton proved
nothing about the document on disk. Every new test reads the real on-disk artifact
from a real campaign.
ANTI-RECURRENCE: `test_no_design_matrix_key_is_undeclared` asserts the subset
relation — every written key is declared — at all three nesting levels (top, row,
row.apply), over every matrix on disk, across two campaigns. Subset rather than
equality because `paired`/`held_fixed` legitimately appear only in some campaigns.
Mutation-verified: injecting one bogus payload field fails 6 tests; removing it
passes 6.
Also fixes `parallel_arms.run_units`, which validated `max_parallel < 1` and then
ran a plain sequential loop — the parameter was accepted and ignored, and its
"results in the same order as units" promise held only because nothing was
concurrent. Now genuinely bounded and concurrent, with ordering guaranteed by
pre-sized positional slots rather than by append order, following
`execute_design`'s pattern. The neighbour-contention argument that pins
`execute_design` to 1 at spending stages does NOT apply here: units are
worktree-isolated by construction and nothing downstream fits a response surface
over them, so `merge_unit_results` only reduces per-unit statuses. That asymmetry
is documented in the docstring since it is the non-obvious part.
Ordering is tested by scripting unit 0 to sleep longest, so completion order is
the exact reverse of submission; an appending implementation would return units
reversed and `merge_unit_results` would silently mis-attribute every seed rather
than raise.
`docs/data-model.md` §7a carried the identical drift and is corrected.
Tests: 11 new behavioral (on-disk artifacts, returned order, observed wall clock).
Full suite 2119 passed, 1 skipped (from 2108).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…redicate
The sharpest form of the fail-loud rule, and the one a fail-loud wrapper misses.
A measured campaign shipped rows of this shape:
max_sustained_rate = 2.1562 # "this load was sustained"
backlog_slope = 0.1234 # the growing threshold was 0.060
The two lines contradict each other — the reported answer is a point the run's own
recorded diagnostic classifies as NOT sustained. 8 of 12 rows had this shape, every
one biased in the flattering direction, and nothing caught it: exit codes were
clean, the artifact was present and parseable, every manipulation predicate passed,
the schema validated. The harness was loud about failures and silent about a
self-contradiction.
No threshold calibration finds this class of bug. Only an assertion tying the
returned value back to the evidence does, and the section now shows that
assertion.
Generalized rather than tied to one objective: whenever an objective is defined by
a PREDICATE OVER A DIAGNOSTIC — "the largest input for which the system is
stable", "the smallest setting that still converges", "the highest load meeting a
bound" — the returned extremum must be re-checked against that predicate before it
is reported. A search returning a point that violates its own acceptance test has
a bug in the search, not a measurement worth recording, and it must fail hard
because as data it is indistinguishable from a good result.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… factors matter
Closes the last three validation gaps this field test found, all by running real
campaigns.
RULE 12 checked only ONE of the three locator styles the runner supports.
Reproduced: `tests/x.py::test_foo` was checked, while `TestFoo` and
`go test ./pkg -run TestFoo` were BOTH silently skipped by an extension-suffix
filter. The bare identifier is what the Go result parser actually matches on, so
the rule was inert for the style that works — and a campaign declaring bare names
with no `build` stage validated at 0 errors / 0 warnings, then aborted at verify
after a full run. The defect was not "pytest-shaped"; it was that two of three
styles were indistinguishable from "checked and fine".
Now classified: a path (single whitespace-free token — the guard matters, since
`make check && ./scripts/verify.sh --all` has a head containing both '/' and '.'
and would otherwise be misreported as a missing FILE rather than as
un-checkable); a bare identifier, checked for a DEFINITION (`func TestFoo` /
`def test_foo`) rather than a mention, so a call site cannot vouch for a missing
test; an identifier behind a selector flag (`-run`, `-k`, `--gtest_filter`, ...);
or `unknown`, which now gets its own warning saying WHY it could not be checked.
Also warns on an asymmetry the fix itself creates: a command-style locator now
RESOLVES here but still fails the contract check, because
`runner.match_declared_tests` matches trailing identifiers and never parses a
command line — so the relation is reported "declared but not executed" and fails
closed at verify. Endorsing a locator that verify will reject is worse than the
silence this rule was fixed to remove, so the warning names the remedy: declare
the bare identifier; `test_command` still selects what runs.
--LIVENESS (opt-in, off by default) closes the other two. `--smoke` verified that
manipulation predicates HOLD, never that a factor MATTERS: a factor whose levels
move the objective by less than run-to-run noise passed every check, consumed its
share of a resolution-V design, and contributed only variance. Measured on a real
target, 3 of 8 candidate factors were unusable — two levels aborted the target,
two were config-captured but consumed by no mechanism. A policy hash over such
factors is a pre-registration of nothing.
`--liveness` runs the baseline N times (default 3, seed-varied) for a noise floor,
then every declared level once with other factors at baseline:
* a level that exits non-zero, times out, or emits unparseable output is a smoke
FAILURE naming the factor and the level (the earlier gap: `--smoke` ran only
the first design corner, so an aborting level was caught only by luck, and an
author's harness that reused a stale metrics file reported it as a clean null
result identical to baseline);
* effect size is the objective's RANGE across levels, printed as a multiple of
the floor and flagged under 2x — reported, never refused, since a small-but-real
effect is the author's call.
Cost is `sum(len(levels)) + repeats` — linear, never `prod(...)`. Gated behind the
flag so every existing `--smoke` stays at one run; plain `--smoke` now prints how
many levels it did NOT exercise, so the gap is visible rather than silent.
Confined to pre-flight: `stage_runner.py` and `policy.py` untouched, no if/elif
decides a next stage, no model call added.
Tests: 21 new behavioral (reported issue strings, exit codes, and the target's own
log of which levels a process actually ran — the target is a real executable that
echoes its config and exits 2 on a chosen level). Full suite 2140 passed, 1
skipped (from 2119).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…o raise it
A validity check that reports a problem you cannot then diagnose has done half its
job, and the missing half is the expensive one.
Observed in a real campaign: the adapter validated that its search produced a
well-ordered result and recorded the outcome as counts —
n_sustained: 7 n_growing: 4 monotone: false
The flag is correct; the record is useless. Those counts are equally consistent
with a single point straddling the decision threshold (noise, reported extremum
broadly fine) and with the response not being ordered in the swept variable at all
(reported extremum is not the quantity it claims, and replication cannot fix it).
Telling them apart needs WHICH points fell in each bin, which the artifact did not
carry — so the row had to be re-run at full cost, after the fact, and only because
someone happened to notice the flag.
Generalized past monotonicity: when you add a validity flag, ask what a reader
needs in order to ACT on it and record that alongside. A boolean is a smoke alarm;
the diagnosis needs the floor plan.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Field test:
|
| substantive calls | billed | |
|---|---|---|
| optimization, clean campaign (mechanism exists) | 1 | $1.68 |
| optimization, first campaign (authors the mechanism) | 1 | $6.57 |
| optimization, entire run incl. 2 defect-forced restarts | 3 | $10.21 |
| reflective (1 of 3 iterations completed) | 10 | $53.09 |
5.2×–31.7× depending on which comparison is fair. All three are reported; none is the "real" one alone.
The mechanism is not per-turn efficiency — measured, reflective's execute-analyze ran at $0.102/turn, cheaper than build's $0.131. What differs is structural:
- optimization pays one substantive call, then measures tokenlessly. Marginal model cost per benchmark run: $0.
- reflective pays a
design+execute_analyzepair per iteration, and those phases can fail expensively: a $9.25 design superseded by a retry, a $13.85execute_analyzethat hiterror_max_turnsat 121 turns. ~49% of its spend produced no usable result. - reflective absorbed 10 API-boundary failures (3 SDK-silence aborts, 6
ConnectionRefused, 1 max-turns). The optimization arm had zero — a compiled epoch makes no API calls, so there is nothing to fail. That asymmetry is architectural, not luck.
Axis 2 — correctness: a tie
Both arms independently authored working ARC (Megiddo & Modha four-list, T1/T2 + B1/B2 ghosts). 154/154 and 153/153 of BLIS's own sim/kv tests pass.
The designed discriminator was a stale guard test asserting mustPanic("arc") — implementing ARC makes that assertion false, so an honest arm must update it while a test-defeating one deletes it. Both updated it honestly. Both also wrote an unrequested scan-resistance test that fails if ARC were a no-op wrapper over LRU.
Structural difference only: optimization isolated the mechanism in a new file (357 lines, 178 insertions total); reflective integrated it in place (848 insertions). The trap did not discriminate. With the same strong model on both sides, the kinds were equal here — worth reporting plainly rather than spinning.
Axis 3 — optimality: optimization found a 52% better configuration
| reflective | optimization | |
|---|---|---|
| valid objective measurements | 4 | 11 (clean screen) / 20 (pooled) |
| factors varied | eviction_policy × tier-presence |
eviction_policy × offload_prompt_only × cpu_bytes × device_class |
| best λ* found | 1.3125 | 2.0 |
| its conclusion | "ARC ≡ LRU" | device_class dominates; ARC unresolved |
Both arms are correct, and the optimization arm found more. Reflective's h_main: REFUTED (ARC ≡ LRU) holds — the optimization data agrees ARC is unresolved (|t| ≈ 0.9). But reflective held offload_prompt_only and cpu_bytes fixed at baseline and never varied device_class at all, so the factor with the largest observed effect was outside its design by construction.
The largest effect is counter-intuitive and has a traced mechanism: sata_ssd beats nvme_gen4 (+0.36 sessions/s, ~28%). cascade() pins each CPU block for its write-through duration, so a slower secondary tier keeps blocks pinned longer, preventing CPU-tier eviction and retaining the hot working set. The slow device acts as an accidental retention policy.
Honest limits: on the clean single-estimator screen (n=11, 4 factors, no replication) no individual factor reaches significance — effect directions are stable but attribution is not established. An earlier |t| = 2.30 for device_class came from pooling rows across two search methods, which inflates n without adding independent information. This screen ranks configurations; confirm is what would certify an effect with a residual-regret bound.
So: optimization wins the deliverable by ~52%, because its factorial design included the factor that mattered — not because it searched better within the same space.
What the run found in this PR's machinery
Seven nous_asks, six fixed in code across the commits on this branch (2140 → passing). Three produced plausible wrong results rather than errors, which is the dangerous class:
apply.kind: config_patchwas schema-documented and applied nothing. Every row of such a campaign ran the baseline while the design matrix, runs, and fitted surface all looked real.validatepassed it 0/0;--smokepassed it too, because the run succeeds. Only the run-timemanipulationpredicate noticed — on row 1 of 18, after a fullbuildstage.design_matrix.jsonnever validated against its own schema. Nine undeclared fields includingkind: "foldover"— an entire design family the schema rejected. Every campaign reachingconfirmemitted a pre-registration record that failed its own schema; the existing test validated a freshly-built skeleton, never the enriched document on disk.- Rule 12 silently skipped 2 of 3
native_testlocator styles, including the bare identifier the Go result parser actually matches on — so it was inert for the style that works.
Plus run_timeout_sec (the hardcoded 600 s ceiling blocked any objective whose evaluation is itself a search), max_parallel (bounded concurrency for confirm replicate blocks only, with screen rows structurally forbidden to opt in), parallel_arms.run_units honoring the max_parallel it previously validated-then-ignored, and --liveness (verify factors matter, and catch a declared level that aborts the target — 3 of 8 candidate factors here were unusable).
The finding I'd most want a reader to take away
My target-side adapter had more defects than Nous did — seven — and they were more expensive. The worst: I edited its output schema three times mid-epoch, so rows measured before each edit carried null for the new keys, and a None reaching float(raw) killed an entire iteration at fit time after ~2 hours of measurement.
Nous already enforces instrument stability for the campaign side: policy.json is content-hashed and _load_or_compile_policy hard-aborts on mismatch. There is no equivalent guard on the adapter — yet the adapter produces every number the pre-registration is about. That asymmetry let me repair the instrument while it ran, three times, and destroy an epoch's comparability with nothing firing.
Guards for this are in progress: an adapter contract hash (fingerprint the response keys+types on the first successful row, hard-abort on drift), output freshness (reject a response byte-identical to the previous row's when levels differ — the defect where a panicking level read as "identical to baseline"), and a declared response.self_check so an author states the invariant their objective must satisfy and Nous enforces it per row. One line —
self_check:
- {metric: backlog_slope, op: "<=", value: 0.060}— would have caught 8 of 12 bad rows at the moment each was measured instead of after the epoch. docs/optimization-campaign-guide.md §7 now carries the pre-flight checklist these lessons produced, written target-agnostically.
…aratus
`policy.json` is content-hashed and `_load_or_compile_policy` hard-aborts on a
mismatch, because a pre-registered policy that changed inside an epoch is not a
pre-registration. That covered exactly one half of the apparatus. A
pre-registered design assumes the MEASUREMENT INSTRUMENT -- the author-written
`run_command` -- is fixed for the epoch's duration too, and there was no
equivalent guard on it at all.
A field test of two campaigns against one simulator produced seven adapter
defects and zero Nous-side ones in the same area. Every damaged artifact stayed
schema-valid throughout. Three of those defects are structural rather than
particular to that author, and this commit closes each:
GUARD 1 -- contract drift. The adapter's output contract (top-level key names
plus each value's TYPE, never the values, which legitimately change per row) is
fingerprinted from the epoch's first SUCCESSFUL row into `adapter_contract.json`
+ `adapter_contract.sha256` at the work-dir root, and re-checked on every later
row. `null` is its own type name because the real defect's signature is a key
still PRESENT whose value became null -- a key-set-only fingerprint reads that as
no drift at all. `bool` stays distinct from `int` and `int` from `float` for the
same reason. Drift hard-aborts exactly as a `policy.sha256` mismatch does, and
`stage_runner` converts it to `OptimizationAborted` rather than routing it to the
`exception` branch: that branch still returns an action from the fitted surface,
and a surface fitted over two instruments has no action to certify.
An ADDED key aborts too, deliberately. A warning would be right about the row
and wrong about the epoch -- the rows damaged by a mid-epoch adapter edit are the
ones measured BEFORE the new key appeared, and no re-run repairs them. It would
also make the guard order-dependent on which side of the edit the first
successful row landed. An apparatus change is an epoch boundary, not an edit.
GUARD 2 -- output freshness. A response byte-identical to the immediately
preceding row's while the levels differ fails that ROW (a cached or stale read).
Compared over the WHOLE object, never the objective alone: two eviction policies
legitimately measured the identical objective in the field test, and that tie is
now a test. `response.constant_fields` excludes fields that never vary, making
the check stricter on what remains. An adapter that echoes its own configuration
back is structurally immune; that limit is asserted in a test rather than left as
unstated dead weight.
GUARD 3 -- declared self-check. `response.self_check`, the same
`{observable|metric, op, value}` shape and the same `predicates` module as
`manipulation`/`constraints`. Nous cannot know an objective's semantics, but it
can require the author to state the invariant that DEFINES the objective and
enforce it per row. Violations fail only their own row -- in the real defect 4 of
12 rows were sound -- with verdicts recorded in `runs.jsonl`'s `self_check` on
passing rows too, so a reader can tell "the invariant held" from "none was
declared". `--smoke`/`--liveness` evaluate them, so a violated invariant surfaces
before a policy hash is written. Validator rule 20 rejects a trivially-true
self-check and one over the primary metric itself.
All three are pure Python at `execute_design`'s new `adapter_guard=` seam. No
model call, no next-stage decision, no `if`/`elif` added to `stage_runner`.
51 new behavioral tests, driven through real shell and Python adapters whose
output shape changes between invocations. Suite: 2191 passed, 1 skipped.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The three guards' logic reads only author-declared keys through the shared `predicates.evaluate` -- no metric name is hardcoded anywhere, verified. But the `self_check` schema description and validator rule 20's error string both illustrated the feature with a single domain's metrics, and those two strings are read at precisely the moment an author is writing a campaign for some OTHER system. An example is not neutral: it teaches what the feature is for. Both now lead with the general case -- an objective that is the EXTREMUM OF A FEASIBLE SET, where the adapter had to decide membership, so a bug in that decision yields a flattering number with no outward sign of being wrong -- and give two examples from different domains (a numerical solver's coarsest converged mesh; a queueing system's highest non-growing arrival rate) rather than one. The module docstring does the same before naming the field-test defect that motivated it, which stays as the code's provenance. No behaviour change. Suite: 2191 passed, 1 skipped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Correction to the field-test comment above — Axis 3 (optimality) restated, not withdrawnI re-read the reflective arm's The two factual errors1. "reflective … never varied 2. "optimization wins the deliverable by ~52%" — not a like-for-like ratio.
No shared level, a 2.5× gap between the ranges. A single ratio across that gap The objective both arms maximizeSame quantity, same instrument, both arms: λ* — the highest session-arrival Held identical across arms: BLIS at How the two arms are faring
Each did what its kind is for, and the two outputs are different in kind:
Whether 2.25 reflects better navigation or a friendlier starting neighborhood is Honest limits on the optimization number. Each of the 15 complete screen rows What is unchanged
Field-test caveats worth recordingThe CPU-level mismatch was my authoring choice, not a property of either |
…an invariant layer that has teeth A field test of `kind: optimization` against a simulator ran ~14 hours and produced ZERO usable output. Not one bug: three failed rows of eighteen aborted the whole iteration and discarded the fifteen valid ones, four times over, so the epoch never reached `confirm` and `transitions.jsonl` stayed empty. Everything here comes from that failure or from the audit it prompted. WHAT NOW FINISHES. A partial design fits instead of aborting. `_fitting_responses` raised for any row that failed to measure while `_fit_and_recommend` a thousand lines below already implemented "refit on the completed subset and report the reduced resolution" -- the very sentence the abort message named. Two guards over one condition, disagreeing, stricter one first. The refit now reaches failed rows and enforces three floors, not one: arithmetic (enough rows), IDENTIFIABILITY (a factor with fewer than two surviving levels has no estimable coefficient -- it is dropped and named, never silently fitted), and RANK, which mutation testing found and which no amount of design would have: keeping six corners of a 2^3 screen passes the per-factor level check and then makes XtX singular against a seven-term model, raising a bare ValueError out of `run_stage`. Interactions are dropped, main effects kept, `interactions_dropped` recorded. EXCLUSIONS ARE TESTED FOR BIAS. Two rows of the real screen timed out, both at one eviction policy's level; both rows at that identical corner under the other level completed. A perfect 2x2 separation that nothing observed, deleting exactly the region where the mechanism under study did the most work. `exclusions.py` reports per-factor and per-cell concentration and WITHHOLDS global certification, because a level-correlated loss is evidence against delta_screen's premise that screening did not exclude the true optimum. The trigger is deterministic, not a p-value: Fisher's exact is valid at n=8..18 but its smallest attainable one-sided p on the motivating pattern is 0.07, so a significance gate could never fire on the defect it exists to catch -- it would launder it as a checked null. The exact tail is reported alongside, never consulted. `infeasible` rows are excluded from the bias count deliberately: a constraint boundary concentrates BY CONSTRUCTION, and counting it would false-flag every constrained campaign. PARALLELISM, GATED BY MEASUREMENT RATHER THAN BY PLATFORM. Spending stages can now run concurrently, default `min(4, cpu_count-2)`. The CPU-pinning escape hatch the old docstring named was rejected on the merits: a disjoint CPU set leaves L3, memory bandwidth, disk queues, page cache and the GPU shared, and every objective in `examples/` is a throughput or tail latency measured through exactly those -- it would have written `cpus: [0,1,2]` into the artifact while the dominant contention channel stayed open. A baseline-corner contention probe was rejected too, and measured: inflation 1.0% at the baseline corner, 9.2% at the saturating one, so such a floor certifies the design it gets wrong. `contention_probe_levels` is author-named and never defaulted. `load_independent: true` is an author claim, recorded as theirs and falsified afterwards from `runs.jsonl`. Isolation is portable and unconditional at every width: `NOUS_RUN_DIR` / `NOUS_ROW_INDEX` / `NOUS_RUN_SLOT`, which closes the field test's worst near-miss -- two rows sharing one `go build -o` path, producing plausible numbers from the wrong binary. Measured 3.6x on an 18-row screen; confirm untouched. CAMPAIGNS THAT REPORT AND RECOVER. Resumability carries the prior attempt's REGISTERED design forward wholesale rather than matching two independently-derived ones -- seeds are iteration-derived, so level-matching would substitute one experiment's randomness for another's. Six guards refuse reuse; `confirm` can neither donate nor receive, at three layers, because two confirm rounds share `kind: shortlist_replicate` and reusing round 1's replicates would record `bonferroni_one_sided_t_paired` for a bound whose pairing premise had failed. Measured 83% wall-clock saving. A repeated-failure breaker halts on three identical failures (the real case wasted four iterations) from the campaign loop, never the policy -- a failed iteration produces no observations, which is why it repeats. `progress.json` + `nous progress` answers "what stage, how far" that no artifact could. Orphan reaping via process groups: an SDK process billed 18 hours after its campaign was killed. `max_wall_clock_hours` ends the epoch through the normal terminal path so the ladder still names an action. TWO PRE-REGISTRATION BYPASSES, CLOSED. Both hash guards read `if sidecar.exists() and <mismatch>`, so DELETING `policy.sha256` skipped verification rather than failing it, and nothing regenerates it. Verified end to end: with the sidecar removed and `screen`'s default transition rewritten, the epoch ran to completion, skipped terminal discrimination, wrote no `confirmation.json`, and recorded the TAMPERED hash as the registration. Same hole in `adapter_contract.read_contract`. Absence and disagreement are the same failure and now abort alike. `fit_effects` also refuses a NaN response at its own boundary: one NaN poisons the intercept and every coefficient, and because every comparison against NaN is False the result reads as "nothing significant" rather than as an error. THE TESTING LAYER: 2191 -> 2874. Metamorphic (run-order invariance, relabeling equivariance, row-drop widening, rescaling, direction symmetry), state-transition including a hypothesis RuleBasedStateMachine over the epoch's pure total `step`, contract tests over the whole artifact chain, boundary/equivalence over ten decision boundaries, a pairwise covering array, and 63 registered invariants across eight types and six levels with 41 checkers and an anti-drift test binding the document to the registry. Real mutmut: 100% kill on `policy.step`, `check_policy`, and both regret bounds. The techniques earned their place by finding what review did not: the rank floor, the `role` enum disagreement (two schemas describing one field, so every confirm row on disk failed its own schema), the NaN boundary, and the confirm-reuse hole. Two honest results are recorded rather than buried. Metamorphic testing has a STRUCTURAL blind spot -- relations compare two runs of the same code, so a uniform sign or scale error cancels; halving every coefficient and inverting the certificate's sign both survived, and oracle anchors were added to close it. And the spec's claim that a paired CRN bound is never wider than unpaired was registered, refuted three times, and demoted: at shared_sd=0.3, noise=3.0, n=4 it is wider in 360 of 600 trials, because pairing removes shared variance and also collapses df. An example test written from the spec's own rationale would have used four replicates and passed. The authoring guide gains three domains that are not this simulator -- a PDE solver's coarsest converged mesh, compiler flags at bit-identical output, an ML training budget -- plus the lessons the field test paid for: prose advice does not hold (its own author violated the timeout section three times), a per-row limit that binds harder on one level biases the surface against it, design for partial survival, and an apparatus change is an epoch boundary rather than an edit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…Os answered
Every item here was DISCLOSED by the invariant inventory rather than found by a
test failing, which is the inventory doing its job: an honest open-violations list
is worth more than a green suite that hides them.
1. THE TERMINAL BOUND CERTIFIED FROM ZERO INFORMATION. On bit-identical
replicates -- spec §3.5's MEASURED deterministic-target case -- the paired
differences are constant, the variance is 0, the t-interval collapses to its
point estimate, and the bound returned `value=0.0` labelled
`bonferroni_one_sided_t_paired`: exact epsilon-optimality asserted from no
variance at all, wearing a real certificate's name. Its sibling
`model_regret_bound` already returned `None` at `pure_error_df <= 0`; the
asymmetry was the defect.
The reach is wider than "three identical numbers", which is why a count check
on replicates could never have caught it: any two finalists separated by a
CONSTANT OFFSET give paired differences of zero variance even when neither
finalist's own samples are constant. Both cases now return `None`/`"none"` with
a detail naming the reason. A deterministic target's honest answer is "this
instrument cannot produce a variance estimate", and the fallback ladder already
handles it -- `terminal_best` names the winner without claiming a bound.
The machine-checkable symptom, preserved as a test: a genuine bound RESPONDS to
its error budget. The old one returned 0.0 for every delta.
2. ZeroDivisionError ON THE CERTIFICATION PATH. The Welch df denominator
`(vk**2)/(nk-1) + (vb**2)/(nb-1)` was guarded by `if (vk + vb) > 0`, but
`vk ** 2` UNDERFLOWS to exactly 0.0 long before `vk` does -- so the guard passed
while the denominator was zero. Minimal reproducer, found by a property test
rather than by construction: replicates `[-3.117993501313441e-82, 0.0]`. A
campaign reporting a normalized rate or a fraction can reach it. The guard now
tests the DENOMINATOR itself rather than a quantity that merely implies it, and
the input class returns a not-estimable bound instead of raising.
3. THE ADAPTER CONTRACT'S null GUARANTEE WAS ONE LEVEL DEEP. Nested blocks were
summarized by key NAMES only, so `{"telemetry": {"rate": 2.0}}` and
`{"telemetry": {"rate": null}}` fingerprinted identically and `diff_contract`
reported no drift -- defect 7's exact real-value-becomes-null signature, one
level down, in a place campaigns genuinely read: `predicates._resolve` walks
dotted paths, so `telemetry.rate` can be an objective or a `self_check`
observable. Nested keys now carry their types to a bounded depth
(`_MAX_NEST_DEPTH = 2`), which covers the shapes real adapters emit while
keeping the fingerprint far smaller than the payload. Below the cap it falls
back to key names, and that limit is stated rather than implied.
`CONTRACT_VERSION` 1 -> 2.
TESTS UPDATED, NOT DELETED. Six tests pinned the pre-fix behaviour, each written
with an explicit note saying what to do when the fix landed; every one was updated
as instructed rather than removed, so the evidence survives the resolution. One
strict `xfail` became a live check. `_welch_df_underflows` keeps its name and its
detection -- the arithmetic class is unchanged, it is simply no longer a crash --
because the properties still need to exclude an input class that has no bound to
shape.
One fixture change worth naming: `_runner` gained an optional seed-dependent
`noise` term, and the confirm test that asserts the terminal bound now passes it.
That is not a workaround. With identical replicates the paired variance is zero and
the bound now correctly declines, so a deterministic stub can no longer stand in
for the fresh-sample comparison `confirm` exists to make. A real target's
replicates differ; the fixture now says so.
THE GUIDE'S TWO REMAINING TODO(cross-ref) MARKERS ARE ANSWERED, against the landed
code rather than the anticipated shape. §7.1a gains the `runs.jsonl`
instrumentation table -- `failure_kind`'s closed vocabulary makes the
budget-versus-defect split a group-by rather than a substring match on `error`,
`last_attempt_ms` is what a per-row ceiling applies to while `duration_ms` is what
a schedule must budget, and `0` is reserved for "did not run". §7.1b gains
`fit_exclusions.json`'s field map and states which of the three "what was lost"
rows are now detected automatically (the unestimable coefficient and the
concentrated loss) versus which remains the author's judgement (whether the missing
region was the one that mattered).
The TODO's suspected code gap turned out not to be one, and the guide now says so:
`effects.json`'s `dropped_factors` means MEASURED NULL (the interval contains zero
-- a result) while `fit_exclusions.json`'s `non_identifiable_factors` means NEVER
ESTIMABLE (the design lost the contrast -- a hole). The two are already recorded
separately; reading a hole as a result would report "this factor does not matter"
about a factor that was never measured.
Suite: 2875 passed, 0 failed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ed first
`invariants.open_violations()` still returned five IDs after the fixes landed --
INV-SEM02, INV-STAT05, INV-STAT12, INV-TMP08, INV-PROV01 -- because clearing a
flag is a separate act from fixing the code, and an earlier attempt to clear them
matched nothing (the flags are positional, not keyword-addressed). A registry that
reports a violation as open after it is closed is as misleading as one that hides
an open violation: a reviewer reads it as "do not rely on this guarantee" and
routes around a guarantee that now holds.
Each was reproduced, fixed, and its ABSENCE reproduced before the flag moved:
* zero-variance replicates now yield value=None / method="none", not 0.0
wearing a paired-t label;
* the subnormal-variance input returns a not-estimable bound instead of raising
ZeroDivisionError;
* a missing `policy.sha256` aborts (verified by driving the real
`_load_or_compile_policy` against a deleted sidecar);
* the empty-`transitions.jsonl` case is closed by partial-design fitting, since
an iteration that loses rows now completes and records its transition.
The class docstring records the discipline rather than only the state: do not clear
a flag because a fix is believed to have landed.
`docs/optimization-invariants.md` said "Open violations (5)" and named them, which
is now false, so it gains the closure notes -- what each violation was, and what
closed it. Written as PROSE and not a table on purpose: my first version used a
table, and `test_document_and_registry_do_not_drift` immediately failed with "these
IDs are declared in more than one table row ... a reader cannot tell which row is
authoritative." That is the anti-drift test catching a real ambiguity introduced in
the same session it was written, which is the best evidence I have that the
document-versus-registry binding is worth having.
Suite: 2875 passed, 0 failed. Zero open violations.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The `kind: optimization` docs, schema, and tests carried identifiers and
prose lifted verbatim from a private target repo's campaigns: a strategy
domain's threshold knobs, its regime names, its objective metrics, and one
campaign name. None of it was load-bearing — every one is an *example* of a
structural point (multi-level factors, a quoted sentinel level, a
cross-factor inertness relation, a multi-regime conjunction, a held-out
split that must not be fitted). The structure is what the docs teach; the
vocabulary was incidental, and it disclosed a private target.
Re-expressed against a neutral threshold-classifier target. The
substitutions are one-for-one, so every structural point and every worked
number survives unchanged:
campaign alert-threshold-robustness -> alert-threshold-robustness
target event-replay -> event-replay
factors escalate_low/escalate_high -> escalate_low/escalate_high
severity_boundary -> severity_boundary
decay_guard* -> decay_guard*
regimes burst/drift/steady -> burst/drift/steady
metrics *_score -> train_score / held_out_score
continuous_max_regret -> continuous_max_regret
leakage class holdout-selection -> holdout-selection
The 5x6x3x5x3 = 1350-cell grid, the ~40-run restatement, the `off`
sentinel's YAML-1.1 boolean trap, and the two anti-patterns built on the
held-out typo all read exactly as before. The typo example keeps its point
by misspelling the neutral name instead (`held_out_scoer`).
Verified: 186 tests pass across the optimize/validate/schema suites, all 43
YAML examples in the two rewritten docs still parse, and no reference to the
old campaign name remains anywhere in the tree.
… is told the mechanism must be cheap Two defects in `build`, both found by a two-arm field test that they confounded. **1. `optimization.guidance.factor_nomination` reached nobody.** `build_prompt` read only `research_question`, `target_system.description`, the two commands, `repo_path` and `locked_parameters`. The string "guidance" appeared zero times in all of `orchestrator/optimize/`, and the guide documented the field as "reserved, not read by any stage" — so an author following the docs had no way to tell the stage that writes the mechanism anything at all. What that cost: an author put the target's known crash mode — "naively skipping this call raises IndexError because the shared buffer's per-frame claim goes unmade" — into `factor_nomination`, reasonably assuming a field named *guidance* reaches the agent being guided. The build shipped exactly that defect. The reflective arm of the same comparison carried the same facts in its `research_question`, which IS its prompt, and avoided it. A 10x optimality gap between the two kinds was therefore partly an artifact of which YAML field the author happened to pick. `factor_nomination` is now passed verbatim as "AUTHOR'S GUIDANCE ON THE MECHANISM". `interpretation` is deliberately still withheld: it steers how *results* are read, and `build` makes no correctness judgement — feeding it the interpretation rules would invite it to pre-judge the measurement `verify` exists to make. The test asserts both halves. **2. Nothing told the build that a slow mechanism is a failed mechanism.** All five REQUIREMENTS were about correctness, and BUDGET DISCIPLINE told the agent that probing "buys no measurement" — so for a campaign whose objective is time, the prompt never mentioned time. What that cost: a build authored a dirty-tracking skip that removed 70% of the per-item work and ran 23.7% SLOWER, because its per-frame decision rebuilt a state tuple over every one of the same N items it was trying to skip. Correct, well-tested, and a regression — which the campaign then correctly measured and recommended disabling, having no further model call with which to reconsider the implementation. A sixth requirement now carries the campaign's own objective (metric and direction) and requires the asymptotic cost of *deciding* to take a fast path to be strictly below the cost of the work avoided, with the O(1)-counter hoist and the avoid-the-run-not-each-call shape named. This is not redundant with any oracle: every `native_test` and all four build oracles check that the mechanism is RIGHT, and nothing in the gate checks that it is FAST. Docs: the guide's `guidance` section said "reserved, not read by any stage" for both slots and now states which one reaches `build` and why the other does not; the `build` section documents the cost requirement and the corollary that a correctness relation cannot catch a slow mechanism. CLAUDE.md records both. Verified: 2877 passed, 12 skipped. The 2 new tests fail against the prior prompt, and the rendered prompt was checked against the real campaign that hit both defects.
…rrency, and sees its constraints Follow-up to 4a39138, which added a "be cheap" requirement to the build prompt. That version was overfitted to the defect that prompted it — a time objective whose mechanism was a per-frame skip. It said "skip", "per-item" and "O(1) counter" four times and mentioned memory zero times, so a campaign minimising resident bytes, or one whose mechanism is not a skip at all, got advice in the wrong currency. Two changes: **Currency-agnostic.** The requirement now asks for the cost of the mechanism ITSELF, stated asymptotically in the same currency as the objective, against the cost it removes — overhead strictly smaller than savings. The time case (the decision path; a check that walks the same N it skips cannot pay for itself; avoid the RUN not each call) and the memory case (resident state added; per-item bookkeeping that scales with N) are given as instances of that rule rather than as the rule itself. **`response.constraints` now reach the prompt.** They did not before, and they are part of what "optimal" means: a mechanism that buys the primary metric by spending a constrained budget is infeasible, not optimal. The declared constraints are rendered into requirement 6 as budgets the mechanism must not spend. Also fixed two of my own tests that asserted on exact prompt wording and broke when a phrase straddled a line wrap — they now normalise whitespace and assert substance. A test that fails because of where a line happens to wrap is testing the formatter. Guide and CLAUDE.md restated to match, keeping the corollary that no oracle checks speed: every native_test and all four build oracles check the mechanism is RIGHT, and a costly one surfaces only at `screen` as a main effect with the wrong sign, after the one build call is spent. Verified: 2878 passed, 12 skipped. The new test fails against 4a39138's prompt.
…s on a checklist `kind: optimization` is frugal BY DESIGN — one substantive model call, every state after it tokenless. The build prompt was nevertheless headed "BUDGET DISCIPLINE" and told the agent that "exploratory scripts, grid searches, and attribution probes are spend that buys no measurement". That spends the kind's structural saving on the single call that determines every downstream number, and it is precisely the wrong instruction for an agent that must establish whether its own mechanism is cheaper than the work it avoids. Measured consequence, three builds on one target: the build that never received the cost facts removed 70% of the per-item work and ran 23.7% SLOWER, having never timed its own decision path. **SCOPE replaces BUDGET DISCIPLINE.** The prompt now states the saving is already banked, licenses exploration explicitly (profiling, counting calls, timing two candidate implementations against each other), and defines what is out of bounds as wrong ACTIVITIES rather than a cost ceiling: do not search the declared factor LEVELS for a winner, do not tune the campaign's knobs to a result — `screen` and `confirm` do that under a design fixed before any result was seen, which is what makes their answer admissible. The three genuinely useful rules survive (don't chase a spec's reference numbers, don't hunt for absent behaviour, clean up probes). **And it ends on a checklist rather than prose.** Twelve items the build must answer in its summary — C1–C6 correctness (declared tests discoverable and passing; the control level bit-identical; unrecognised values fail loudly; surrounding invariants preserved on the fast path; changed tests updated not deleted; new tests fail against the unmodified tree) and O1–O6 optimality (overhead strictly smaller than savings in the objective's currency; deciding cheaper than the work skipped; cost paid once not per measurement; algorithm over constants; no constrained budget spent on the primary; state the assumed regime). "n/a, because ..." is an answer; silence is not. This is §7.9's own argument applied to the build stage: each item is a defect a real build shipped, and prose advising against several of them was already in the guide when they shipped anyway. Verified: 2878 passed, 12 skipped. The replaced test now FORBIDS the frugality framing it used to pin, while still requiring the surviving scope rules — and it bans the instructions, not the noun, so the prompt may still explain WHY thrift is unnecessary.
…d` authors it
Measured motivation, three builds of the same mechanism on one target, same
objective, same adapter:
* a build whose prompt never received the cost facts removed 70% of the
per-item work and ran 23.7% SLOWER — its per-frame decision walked the same N
it was skipping;
* the same build given those facts reached +3.65%, certified;
* the reflective kind, which DESIGNS FIRST, reached -10.4% and named the winning
architecture in its design artifact before writing a line of code.
The reflective arm's advantage was not a bigger authoring call. It was a separate
call that priced the mechanism first. Its bundle was 29.4K characters, of which
87% was experiment design — hypothesis arms, locked parameters, run plans — that
`kind: optimization` already carries pre-registered and content-hashed, which is a
strictly stronger anti-p-hacking guarantee. Only the remaining fraction produced
the better mechanism, so this stage captures that fraction and nothing else.
`plan` is opt-in, legal only as position 1 immediately before `build` (rule 11a),
spends one call, reads the target, writes no code, and produces schema-checked
`mechanism_plan.json`:
cost_model where the cost actually is, in the objective's currency, with
numbers read off the target — not derivable from the YAML
approach the strategy plus BOTH halves of the comparison the build's
O1/O2 items demand: cost_of_deciding and cost_avoided
rejected >=1 alternative, priced. The field that catches "my check walks
the same N I am skipping" while catching it is still free
failure_modes symptom / cause / guard — a named crash mode with no guard is
exactly how one build shipped one
`check_plan` gates the artifact: an unparseable reply or a section that is present
but vacuous raises `PlanRejected` and writes NOTHING, because `build` reads this
file as its specification and a half-formed plan on disk is worse than none. The
plan then renders into the build prompt as `MECHANISM PLAN` — rejected
alternatives included, so the build does not re-derive a loser already priced.
FRUGALITY IS PRESERVED, which is the point. Both `plan` and `build` are pre-epoch:
`step()` can never route to either, so the compiled epoch stays at zero model
calls. Substantive calls go 0 (neither declared) -> 1 (`build`) -> 2 (both),
against the reflective kind's nineteen on this campaign.
INV-ECO01 (`check_no_model_call_reachable_from_epoch`) is NARROWED, not disabled:
its allowlist now names the pre-epoch PAIR rather than `build` alone, and its
docstring says why. Verified by negative control — injecting a dispatcher import
into `decide.py`, a real epoch state, still produces exactly one violation, and
removing it returns clean.
Tests (38, behavioural, no live LLM — injected at plan's own `sdk_runner` seam):
contract the artifact `build` reads; the plan reaching the build prompt;
byte-identical prompt when no plan exists (opt-in must stay opt-in)
property every required section swept for absence; four vacuity shapes
([], {}, "", None); 1/2/5 rejected alternatives
metamorphic adding detail never invalidates a plan (direction reasoned first:
the checker rejects MISSING and VACUOUS, so more content can only
remove reasons); truncating a rationale toward empty is monotone
non-increasing in acceptance
mutation currency, cost_of_deciding, cost_avoided and guard each asserted
separately, so deleting any one checker line fails one named test
hostile non-JSON fails closed with no artifact; fenced JSON still parses;
a structurally invalid plan is rejected not written; cost is logged
to llm_metrics even when the plan is then rejected
end-to-end the real `run_stage` dispatches `plan`, writes the artifact, and
never terminates the run
Verified: 2916 passed, 12 skipped.
…an being write-only The `plan` stage as shipped in 9cae1b8 had a gap I flagged in its own review: the plan was consumed only by `build` and then never checked. A plan that predicted "this mechanism pays" against a screen that measured a regression passed silently — which is precisely the defect the stage exists to prevent, surfacing one stage later and unremarked. `check_plan_against_effect` closes it. The plan asserts `cost_avoided > cost_of_deciding`, i.e. that enabling the mechanism moves the objective the way `direction` calls better; `screen` measures exactly that as the mechanism factor's main effect. When the two disagree by more than the workload's own noise floor, `effects.json` gains a `plan_contradictions` entry naming the factor, the measured effect, both sides of the plan's cost claim, and the floor it cleared. Three deliberate choices: * REPORTED, NEVER FATAL. A refuted plan is a finding — the campaign's own screen did its job, and the honest outcome is a recommendation that leaves the mechanism off plus a note that the cost model was wrong. Aborting would discard a correct measurement. * ON `effects.json`, not a sibling file. Same reasoning `exclusion_balance` already documents there: a caveat in a file the reader may never open is not a caveat, and `project_findings` derives its prose from that artifact. * SILENT INSIDE THE NOISE FLOOR. Below the floor the measurement cannot refute anything, and claiming otherwise manufactures findings — the same error as reading a 1% difference as real when run-to-run variation is 2.4%. `write_effects` gains three OPTIONAL arguments (`work_dir`, `direction`, `noise_pct`), so every caller written before `plan` existed keeps working and produces a byte-identical payload — asserted by a test. Tests (13 more, 51 in the file): property direction honoured in BOTH senses — minimize and maximize, each with a helping and a hurting effect. Reasoned before asserting: a checker that hardcoded "lower is better" would silently invert on every maximise campaign, and most of the corpus maximises. property three effects inside the noise floor, none flagged contract effects.json carries the flag; carries none when borne out; byte-identical payload when the new arguments are omitted boundary no plan means nothing to falsify (opt-in stays opt-in) Verified: 2929 passed, 12 skipped.
Field test:
|
| arm | kind | what it received |
|---|---|---|
opt |
optimization | the campaign as first authored |
optp |
optimization | parity re-run — identical campaign, but the build received the same facts refl got, after 4a39138 fixed the channel |
refl |
reflective | 4 iterations, its own DESIGN phase each time |
Why optp exists — a confound I introduced, then measured
opt's build never received the target's cost facts. I had written them, into
optimization.guidance.factor_nomination. build_prompt never read that field
— the string "guidance" appeared zero times in all of orchestrator/optimize/, and
the guide documented it as "reserved, not read by any stage." refl carried the
same facts in research_question, which is its prompt.
Measured asymmetry: opt's authoring text 1,445 chars, refl's 4,130 (2.86×). Of 13
measured facts, opt's build received 3 and refl's received all 13.
optp is the same campaign re-run with that channel fixed and 0 fact
asymmetries (verified by rendering the real build_prompt and diffing).
(a) Cost
| arm | USD | calls | model time | output tokens | cache reads | $/measurement |
|---|---|---|---|---|---|---|
opt |
$7.30 | 8 | 28.8 min | 105K | 5.2M | $0.059 (124 runs) |
optp |
$12.98 | 5 | 44.0 min | 140K | 13.0M | $0.481 (27 runs) |
refl |
$71.23 | 26 | 271.5 min | 700K | 67.6M | $0.250 (285 runs) |
refl spent 5.5× optp and 9.8× opt. Its design phase alone ($27.16) cost
more than both optimization arms combined. Both optimization arms spent 100% of
their tokens in build — every other state is tokenless, which is the kind's
structural property, not an accident of this campaign.
(b) Correctness
| arm | tests | tree diff | test lines deleted | verdict |
|---|---|---|---|---|
opt |
21 pass (from 9) | 3 files, +294 | 0 | clean |
optp |
13 pass | 9 files, +534 | 12 | net-positive refactor |
refl |
53 pass | 9 files, +636 | 12 | net-positive refactor |
The 12 "deleted" lines in optp and refl are both the same change to
tests/render_compare.py: generalising the golden-image harness's config writer
from booleans-only to typed values, and adding flags to drive the new knobs. I read
every line — no guard was removed. Falsifiability verified in refl's tree by
injecting a real pixel defect: 7 tests fail, including its own new
test_dirty_tracking_is_invisible[4]/[16].
(c) Optimality — one interleaved sweep, idle machine
6 rounds, oracle → opt → optp → refl within each round, each arm at its own
recommendation in its own tree. Interleaved because block ordering fabricated a
result twice in this experiment: a 2.5% phantom gap between byte-identical
trees, and a sign flip on opt's optimality.
Ground truth from a pristine third clone: baseline 0.22998 s, floor
0.09825 s → 57.3% headroom.
| arm | recommendation | mean | vs baseline | paired t (df=5) | headroom captured |
|---|---|---|---|---|---|
opt |
DT=false (mechanism off) |
0.23149 s | −0.66% | −1.76 | −1.1% |
optp |
DT=true, SG=mobject, RI=16 |
0.22592 s | +1.76% | +3.18 | +3.1% |
refl |
DT=true, SG=mobject, RI=16 |
0.19351 s | +15.86% | +30.93 | +27.7% |
Note opt's and refl's recommendations are identical configurations — the 15.9%
gap is entirely in the implementation, not the settings.
Certification
| arm | recommendation.basis |
R_terminal | ε |
|---|---|---|---|
opt |
terminal_best |
null (not estimable) |
0.0180 |
optp |
certified |
0.00304 | 0.0180 |
refl |
n/a — the reflective kind has no certificate |
Both bounds came back null for opt rather than 0.0 — "not estimable" rather
than a fabricated guarantee. That invariant held.
(d) Wall clock
| arm | model time | measurement runs | campaign elapsed |
|---|---|---|---|
opt |
28.8 min | 124 | ~50 min (2 epochs) |
optp |
44.0 min | 27 | ~50 min |
refl |
271.5 min | 285 | ~195 min (4 iterations) |
Arms ran concurrently and contend for one GPU (+5% at 2-way, +21% at 8-way), so
wall clock is the least clean axis. Model and measurement time are reported
separately because only the latter is affected.
What the comparison actually shows
1. The guidance gap was real, and fixing it flipped the sign. Same target,
same knobs, same objective: opt's mechanism was a 23.7% regression (t=25);
optp's was certified with DT: true. That delta is attributable to a plumbing
defect of mine, now fixed in 4a39138.
2. It was not the whole story. At the same config, optp captures 3.1% of the
headroom and refl 27.7%. Both agents had identical facts. The difference is
architectural — measured at subtree/16:
| arm | write_uniforms removed |
claim removed |
timing |
|---|---|---|---|
opt |
70% | 47% | +23.7% slower |
optp |
69% | 34% | +3.65% |
refl |
93% | 93% | −10.4% |
opt decided by rebuilding a state tuple over every family member each frame —
an O(N) check to avoid O(N) work. refl pushed invalidation to the source (a new
structure_epoch.py, note_structure_changed() from the mutators) so a frame
reads one counter, and added SharedBuffer.restore_used() to replay claims in one
step instead of 57,600 calls.
3. refl decided this at DESIGN time, in one iteration — it did not iterate out
of opt's failure mode. Its bundle.yaml, written before any code, already named
opt's exact defect as a branch to avoid: "If C2 is SLOWER, the epoch check is
being paid on top of the old path rather than instead of it."
4. And it kept improving. Iteration 4 found an apparatus defect in its own
mechanism via preflight (a missing note_flags_changed() call), fixed it, then
measured a further 2.9% (Wilcoxon p=0.000488, 11/12 seeds).
The structural finding, stated carefully
kind: optimization spends one build call; every later state is tokenless.
When that call produces a sound-but-slow design, the kind has no path to revise it
— it can only measure the regression accurately, which both optimization arms
did correctly. opt recommended turning its own mechanism off; optp certified a
real if modest gain. Neither overclaimed.
kind: reflective deliberates implementation strategy in DESIGN before writing
code, and iterates. That cost 5.5–9.8× more and bought 9–25× the headroom here.
This is what motivated the new plan stage (9cae1b8, 9595d7b): one pre-epoch
call that prices the mechanism before build authors it, capturing the ~13% of
refl's design bundle that mattered (87% was experiment design this kind already
pre-registers and hashes — a strictly stronger guarantee). The epoch stays
tokenless; substantive calls go 1 → 2, not 26.
plan is untested in the field. Its value is a hypothesis until an opt run
uses it. The natural next experiment is a fourth arm with plan enabled and the
same parity facts.
Defects found in nous by this field test
| # | defect | status |
|---|---|---|
| 1 | guidance.factor_nomination reached no prompt; guide said "reserved" |
fixed 4a39138 |
| 2 | build prompt had no performance requirement, and told the agent probes "buy no measurement" | fixed 4a39138 / 1d8f428 |
| 3 | response.constraints never reached the build |
fixed de76fc7 |
| 4 | the plan was write-only — nothing checked it against screen |
fixed 9595d7b |
| 5 | refl's best_found.json scored every candidate 0.0; findings carry empty metric dicts, so its own optimality ranking never populated |
open |
Five further defects were mine, in the campaign or adapter — --json-report
running zero tests, native_test locators, type: numeric on an integer knob
(which cost two epochs), an inverted guard, and two verifications that could not
fail. All are documented in docs/AUTHORING_DEFECTS.md and docs/RUN_LOG.md in
the field-test workspace.
One caveat on attribution: I authored all three campaigns. Some of the opt→optp
delta is my authoring rather than the kind, and I have not separated those cleanly.
The optp→refl delta is the one I'd defend as structural.
…he schema
`stage_runner._plan_max_turns` reads `campaign.max_turns.plan` and falls back to
`plan.DEFAULT_MAX_TURNS`, but `max_turns` declares `additionalProperties: false`
with only build/design/execute_analyze/report. So a campaign that sets a plan
turn budget -- the documented way to give the stage room -- fails validation
with "Additional properties are not allowed ('plan' was unexpected)", and the
code path that reads it is unreachable from any valid campaign.
Found while authoring a `stages: [plan, build, ...]` campaign, which is the
first thing that needs it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`policy._PRE` is what implements "plan and build sit outside the compiled
epoch" -- the claim Stage.PLAN's docstring, CLAUDE.md, and the guide's states
table all make. It listed only ("build", "verify").
Two consequences, from one missing element, both observed on a real launch of a
`stages: [plan, build, verify, screen, refine, confirm]` campaign:
1. `pre_epoch_stages` walks the declared order and BREAKS at the first stage
that is not pre-epoch. With "plan" absent from `_PRE` it broke on element 0
and returned `[]`. `_resolve_state` then took the epoch path for iteration 1,
so BOTH model-facing stages were skipped and `verify` ran first -- aborting
on exactly the correctness relations `build` was declared to author. The
campaign burned two iterations failing that way. So the stage was
unreachable from any campaign that declared it.
2. `_enabled` subtracts `_PRE` to get the epoch's state set, so `plan` leaked
into it. Nothing routed there yet, but a compiled epoch containing a
model-calling state contradicts the kind's central invariant that no model
call is ever made inside one.
Four regression tests: the pre-epoch prefix, `plan` absent from `_enabled`,
neither stage present in a compiled `policy.json`, and the pre-`plan` shape
unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… `plan` Second half of the same defect as f25e382, in a second copy of the same fact. `policy._PRE` was fixed so `plan` is classified pre-epoch; `compiled_from.pre_epoch` therefore correctly began carrying it -- and `policy.schema.json` independently enumerated that array's items as ["build", "verify"], so `write_policy`'s jsonschema.validate then rejected every compiled policy: compiled policy does not conform to policy.schema.json: 'plan' is not one of ['build', 'verify'] (at compiled_from/pre_epoch/0) On a real campaign that failed three consecutive iterations and tripped the circuit breaker. Two copies of "what is pre-epoch" drifted apart, and nothing compiled a plan-campaign policy and validated it -- so the first fix looked complete and moved the failure one stage later. The regression test exercises `write_policy`, not just `check_policy`: `check_policy` covers the closed vocabularies and reachability, NOT the schema's shape constraints, so a test asserting only `check_policy` passes while production still aborts. Verified by reintroducing the enum and watching the test reproduce the exact production error. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…-only `build_prompt(campaign, declared_tests, work_dir=None)` locates `mechanism_plan.json` through `work_dir` and renders it as the MECHANISM PLAN block. `run_build` already HAD `work_dir` as a parameter and called `build_prompt(campaign, declared_tests)` without it. So the whole `plan` stage was write-only in production: it ran, spent its agent call, wrote a schema-checked plan that `check_plan` gated -- and `build` received none of it. Measured on a real campaign: a 26833-character difference in the assembled prompt, silent, with nothing in any artifact to show the build never read its own specification. The build therefore re-derived the mechanism from scratch, including the five alternatives the plan had already priced and rejected, which is exactly the waste `plan` exists to prevent. This is the same class of defect as `factor_nomination` not reaching the build prompt: a field is populated, a stage consumes something else, and the gap is invisible from the artifacts. The test asserts the plan's own text (a sentinel in both `cost_model` and `rejected`) appears in the prompt the runner receives, rather than asserting that a function was called with an argument. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three additions to the build/plan sections, all from one campaign's measured failures rather than from review: 1. `max_turns.build` is a runaway-loop backstop, NOT a budget lever. The corollary of "explore, don't economise" is that the author must not re-impose the ceiling the prompt just removed. Measured across three launches of one build: at 90 turns it wired the mechanism across 8 files with `go build` green and the affected suite passing, and STILL failed the gate with none of the declared tests written (context was clean at ~5K tokens); at 200 turns it died of context exhaustion from tool-output VOLUME (one bare `go test ./...` over 3343 tests). The guide now says plainly that these have different causes and opposite fixes -- output hygiene in `guidance.factor_nomination` for the first, a higher ceiling for the second -- and warns against reading them as one tradeoff. The section's own example raised from 160 to 400 with the reason attached. 2. The declared tests are the contract, not polish. `verify` is fail-closed, so a build that spends its whole call perfecting the mechanism fails exactly as hard as one that never wrote it -- and `--smoke` cannot warn about it, because "declared test does not exist" is the EXPECTED pre-build state (rule 12 correctly stays silent when `build` is declared). 3. `plan` is not idempotent, plus the three defects that lived on the plan -> build seam until a real campaign ran it: `_PRE` omitting `plan`, `policy.schema.json`'s `pre_epoch` enum as a second copy of the same fact, and `run_build` dropping `work_dir` so the plan was write-only. Each entry is written as the symptom an author would actually see, since none was visible from the artifacts. CLAUDE.md carries the same three facts in short form, including the testing rule they imply: a regression test for anything schema-shaped must call `write_policy`, not just `check_policy`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…cted Third instance of one defect family on this branch, after `max_turns.plan` (0fac096) and `policy.schema.json`'s `pre_epoch` enum (a2ea643): production code reads a key that `additionalProperties: false` rejects, so the documented override fails validation and the code path is unreachable from any valid campaign. `_resolve_model` reads `models[phase]` for every phase including `plan` and `build`; the schema declared only design/execute_analyze/report. `models.build` matters beyond per-phase cost, which is why it surfaced now. `build` is the kind's ONLY substantive call, and three launches of one mechanism established that it can exceed a 200K context window: uncapped at 400 turns, with 0 unbounded reads, it still exhausted context at 418 events / 164 tool calls having written 6 of 8 declared tests. ~2200 lines of production + test code wired into a 366-file Go codebase, in one call with no compaction available through the SDK, does not fit. The remedy is a longer-context variant of the same model (`claude-opus-5[1m]`, verified working) -- and that requires exactly this override. Fewer turns truncates the mechanism instead; the guide now says so, correcting the emphasis of bf5a4f9. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ains an MDE check Two pre-flight gaps a real campaign fell into, both invisible to every existing check. 1. THE GENERATED CENTER POINT IS UNREACHABLE BY DECLARATION. Check 2 of `--smoke` runs ONE CORNER (every factor's first level); `--liveness` runs every declared LEVEL. A center point is neither: its numeric coordinates are midpoints that appear in no `levels` list, and its `choice` factors are pinned to their first level because a choice factor has no midpoint (`matrix.expand`'s `center_choice_pinned` already records this — the pinning is deliberate, and this is an authoring hazard rather than a behaviour bug). On a CONSTRAINED factor space the combination can be unrunnable. Observed: a numeric factor meaningful only when a choice factor was ON declared the numeric's OFF sentinel as a level; the generated center paired the numeric's midpoint with the choice's OFF level, the target correctly rejected it as nonsense, and all three center points failed — taking the design's only replication, hence its pure-error estimate AND its lack-of-fit test. Static validation passed, `--smoke` passed, `--liveness` passed. `_center_levels` delegates to `matrix._decode_level` with a synthetic center `DesignPoint` rather than reimplementing the decode rule, and a test asserts it equals what `matrix.expand` actually generates — if the probe and the matrix ever drift, `--smoke` would be probing a configuration the epoch does not run, which is worse than not probing. 2. §7.9 CHECK 14: compute the design's MINIMUM DETECTABLE EFFECT before hashing the policy. With `c` corners and `df` pure-error df, MDE ≈ t(0.975,df)·2·sd/√c. A 2^4 factorial with 3 center points has df=2, so t=4.30 and the MDE is ~2x the pure-error sd. The same campaign declared `epsilon: 3%` against a measured 2.35% sd — unreachable from the screen at any effect size, and its screen duly reported a model bound 53x epsilon. The MDE is a property of the design's SHAPE, not of any field, so no field-level validation can flag it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds
kind: optimization— a factorial / response-surface campaign type alongside the defaultreflectiveone.Refs #120. Design spec:
docs/superpowers/specs/2026-08-13-optimization-campaign-kind-design.md. Authoring guide:docs/optimization-campaign-guide.md.Why
The blog post "Discovery Is Easy, Composition Is Hard" benchmarked seven LLM-driven optimizers on the Certus cold-read path. Six beat baseline; five stalled near half the available headroom. The decisive fact was that batching cost 9.5% in isolation yet was required for the winning compound — so any search that prunes regressing candidates cannot reach the optimum.
That is a property of one-factor-at-a-time search, not of the problem. Factorial designs have no composition barrier because they estimate interaction terms directly instead of composing increments. A resolution-V fractional design over 8 factors gets every main effect and all 28 two-factor interactions unconfounded.
It also addresses the cost complaint: the per-configuration decision moves out of the model and into Python.
What lands
Five stages, one per iteration:
build→verify→screen→refine→confirm. Onlybuildmakes a model call;verify,screen,refineandconfirmmake zero.buildis opt-in and absent from the default order, so every campaign written before it existed behaves identically. Declare it first when the mechanism under study does not exist in the target yet and the campaign has to author it. Substantive model calls per campaign: ~3 withoutbuild, ~4 with it, against 60–90 tokenless benchmark runs.A new
orchestrator/optimize/subpackage (12 modules, all pure functions over data), four artifact schemas, theoptimizationcampaign block with twelve cross-field validation rules, kind-scoped gate defaults, andnous validate campaign FILEfor authoring-time checks.The architectural guarantee
orchestrator/iteration.py: 23 insertions, 0 deletions — one delegation branch plus one comment. A campaign with nokind, orkind: reflective, behaves exactly as before.tests/test_optimize_no_regression.pyproves it structurally (AST-parsed, so formatting changes cannot produce a false pass), including the negative case thatcomplexity_tierremains legal on reflective.Full suite: 1709 passed, 1 skipped.
Correctness
Every campaign declares two mandatory check families per factor: a manipulation predicate (did the lever engage?) and native property/metamorphic tests in the target's own idiom (is the mechanism correct?). Nous checks the contract; the target's runner produces the verdict, so the harness needs no language knowledge and the tests outlive the campaign.
Relations are classified
correctness(hard-fails the campaign) versusbehavioral(recorded as a finding, campaign continues). That split is deliberate: a monotonicity break is a discovery — the motivating lever was −9.5% alone and required in combination — so hard-failing on it would make the campaign blind to exactly what it exists to find.buildmakes no correctness judgement.verifyremains the gate, so the stage that writes the mechanism is never the stage that certifies it, and certification is pure Python reading a real test runner's real output.verifyis fail-closed: a declarednative_testthat did not execute counts as a failure, because "not run" is not evidence of correctness.Three checks fire regardless of gate approval, since auto-approve is this kind's default: design-matrix fidelity drift, a held-out metric reaching a fitting input, and a correctness-relation violation.
Quality guards on the reported optimum
Three layers, added because a real campaign tripped each one:
confirmrefuses a fitted stationary point whose coded coordinates leave[-1, 1]and replicates the best observed configuration instead.decide_after_refinealready detected this and wrote "ranges were too narrow to contain the optimum — escalate before confirming" into findings, butTriggeris reported-not-acted-on, soconfirmdid it anyway.confirmation.jsonrecordsconfirmed_is_best_observedand, when they disagree, the absolute and percentage gap. "The prediction reproduced" and "this is the best configuration found" are different claims.runs/iter-N/test_output.log; a failed configuration's full stdout/stderr lands inruns/iter-N/failed_runs/failed_run_<i>.logwith its row index and levels.On the campaign that motivated these: the fitted optimum was 112.4997 while the campaign had already measured 182.2159 — 38% off, with replicate spread
0.0, reported asCONFIRMED. All three guards now fire on it.Validation
Four real campaigns against
inference-sim, with per-arm git worktrees and an independent third worktree carrying a separate reference implementation for ground truth. Full write-ups in the comments below.--ceiling-curvein Go firstExperiment 4's per-phase split is the durable claim:
design$1.62build$2.44execute-analyze$4.77Authoring code costs roughly the same in both kinds. What this kind removes is the recurring per-iteration measurement cost — 33 real benchmark runs for zero tokens. The advantage therefore scales with how many measurement iterations a question needs, which is why the ratio ranged from 66× (pure measurement) to 2.5× (authoring-dominated).
Honest limits: reflective has never completed in these comparisons (stopped in #3, hung in #4), so every reflective cost figure is a lower bound. And no target has yet shown optimization finding a better answer than reflective on the same question — in experiment 4 it found the joint optimum while reflective isolated a mechanism, a difference in aim rather than in quality.
What running it found
Fourteen defects were found by running real campaigns, none by the test suite — 1600+ tests stayed green throughout, because they inject fakes at exactly the seams that were broken. The full list with consequences and fix commits is in the comments; the ones a reviewer should know about:
test_commandhad no caller;config_runnernever wiredverifyorscreen— the kind was unusable end to endmanipulationassumed targets echo their config backconfirmreplicated the geometric originbuildedited the canonical checkout, not its worktreeruns.jsonlThe last two are worth dwelling on. For the worktree breach,
cwdwas set correctly, so the code looked right — nothing verified where the edits landed. For the report,_format_results_summarywas added by #214 specifically to stop reports dismissing real data as "no data", and it was reintroducing that exact failure for a layout it did not know about.Process note
Nineteen further defects were found and fixed during implementation, recorded with reasoning in
docs/superpowers/plans/2026-08-14-optimization-campaign-kind-EXECUTION-LOG.md. Most were in the plan's own reference code. Three worth a reviewer's attention:confirmsilently re-ran the screen design while the guide claimed it reproduced the predicted optimum.Six of the nineteen were in
orchestrator/optimize/stage_runner.py, the one module without an independent author, and three consecutive review rounds each found another defect in it on a path no test drove. That file is still the one most worth a human read.Known limitations
locked_parametersdeviation is not a hard-fail on this path.validate._validate_locked_parametersexists but is reached only from bundle validation, and the matrix path has nobundle.yaml/experiment_specto compare against. Thestage_runnerdocstring says so rather than claiming a guarantee that does not exist.builddoes not check the spec it is given. It takes the campaign's mechanism description as authoritative and makes one call. In experiment 4 it faithfully implemented a metamorphic ordering I had written backwards; reflective's DESIGN phase caught the error by reasoning about the algebra. An author's algebra error is a blind spot here, which is why the guide now requires a worked numeric example in any metamorphicstatement.buildis one call, not a build-until-green loop. That is deliberate — it bounds cost and stops the model negotiating with its own gate — but it means averifyfailure ends the campaign and needs a re-run.Branch policy: this PR also updates
CLAUDE.mdandREADME.mdto targetmainrather than the divergedreflective.🤖 Generated with Claude Code
Update: compiled experimental policy (paper alignment)
Everything above shipped
kind: optimizationas a fixedbuild → verify → screen → refine → confirmschedule. A companion paper (../papers/nousko/paper.tex, "Estimate, Don't Search: Compiled Experimental Policies for Agentic Systems Optimization") describes a stricter version of the same idea: the epoch is not hard-coded control flow but a compiled, pre-registered state machine —policy.json, hashed before the first measurement — interpreted by a purestep()function over a closed observation vocabulary. Measurements choose among registered branches; they never invent one. A semantic exception ends the epoch rather than crossing the boundary implicitly.Design spec:
docs/superpowers/specs/2026-08-16-compiled-policy-design.md(binding authority). Implementation plan:docs/superpowers/plans/2026-08-16-compiled-policy.md(16 tasks, TDD, executed viasuperpowers:subagent-driven-development— fresh subagent per task, independent task review, controller audit with direct mutation-testing after every task).Status: all 18 tasks complete (16 planned + 2 inserted mid-plan), plus a final whole-branch review. Phases 0–5 of the plan, the build-stage oracles, systems-target readiness, docs, and a final cross-task review pass are all done. This is the final update to this section.
What Tasks 1–11 closed
screen → refine → confirmladder in Python control flowpolicy.json: schema-validated, content-hashed, compiled once atverify;step()interprets it.orchestrator/optimize/policy.py.decide.recommend()— exact enumeration/argmax over the valid candidate space, no model judgment. Fixes both bugs (Task 7).certificate.model_regret_bound/terminal_regret_bound— Bonferroni-corrected simultaneous one-sided bounds,R_δ(x̂) = max_z U_δ(z,x̂), reported honestly asNonewhen no pure-error estimate exists rather than fabricated (Task 8).confirmnow measures a shortlist of finalists fresh and compares them independent of the response surface — the paper's terminal discrimination. Full fallback ladder inreport.json: certified → terminal_best → model → measured → baseline, plusknown_valid_baselinesupport (Task 9).foldoveris a real policy state that spends a measurement block, but only when resolving the alias could change the recommended answer (decide.alias_consequential, quantified over the alternative resolution's ε-optimal set) — the paper's "registered augmentation" (Task 10).epoch_end-<N>.json, and the campaign always returns an action (never an empty/aborted result) even when it hits one. A later fixed re-run recompiles cleanly into a new epoch (Task 11).Every strict
xfail(strict=True)test planted in Task 3 — one per gap above, each naming which future task would flip it — is now a genuine pass.tests/test_optimize_harness.pycarries zero xfails for the first time since Task 3. Full suite: 1915 passed, 1 skipped, 0 xfailed (from 1787 at the start of this phase).Two verified pre-existing defects, fixed early (spec §4)
design matrix is singular) —fit_effectsrequested one column per two-factor interaction, and at resolution IV aliased columns coincide. Fixed by collapsing to one coefficient per alias class.effects.jsonstayed schema-valid. Fixed by fitting on the complete-row subset and recording exclusions (fit_exclusions.json).Genuine judgment calls made during implementation, each independently reviewed and (where warranted) mutation-verified
confirm's terminal-discrimination shortlist, under model rejection (Task 11). The plan's brief specified that when the fitted model is rejected (lack_of_fit),confirm's shortlist should be built from measured-valid rows only, never the model's ranking. Measured directly: on thebowlandslaoracle surfaces, a strict measured-only rule discards the exact interior optimum refine had already found (because it's a model prediction, not yet a measured row) and, on the constrained surface, caps the achievable answer at 6.12% off truth regardless of how much additional confirm budget is granted — a structural dead end, not a tuning artifact, because a measured-only shortlist can only ever re-measure what's already on disk. The shipped rule instead seeds roughly half the shortlist from measured leaders and fills the rest from the model's ranking — every finalist still gets freshly measured before it can win, so nothing untested is ever returned as the answer. Independent review concluded this is required by the paper's actual constraint (never return an unmeasured configuration — a report-time rule) rather than a shortlist-membership rule, and is correct paper fidelity, not a deviation from it.noushas not reached GA, so until the project owner says otherwise, achieving a piece of work's correct design outranks preserving legacy observable behavior anywhere in the codebase (nowCLAUDE.md, top-level, repo-wide — not scoped to this plan or kind). Several of the fixes above changed observable output from whatnouskodid immediately before this phase; each is named and argued in its task's commit, not silently introduced.What Tasks 12–16 and the final review closed
buildcould author code and nothing verified the campaign still measured what it claimed tomechanism.patch/mechanism.sha256snapshot with hard-fail-on-drift inside the epoch, checked against the compiled policy's own recorded hash (Task 12; scoping fix Tasks 13.5/14.5, below).buildstage's authored tests could pass trivially (never actually exercising the mechanism) and its control could silently differ from productionbuildand pass after, or they prove nothing;known_valid_baselinemust equal the pre-build control within tolerance (Task 13).workload.seed_env/workload.seeds— every row in a comparison gets a deterministic seed,confirm's replicates pair by index across finalists, andterminal_regret_boundcomputes a tighter paired bound when CRN is genuinely in effect (Task 14).run_command/test_command, the seed-honesty check, SLA-as-validity) existed only in scattered docstringsdocs/targets.md, a ~300-line contract doc with the actual detection recipe for "did the target really read the seed" (Task 15).policy.json, the six states, the fallback ladder, the artifacts an epoch writes) was implemented but not documented anywhere a human or a future agent would read firstdocs/data-model.md§7, and the policy summary inCLAUDE.md— all describing what the code actually does, not the spec's idealized names (Task 16, below).Two production defects found live, mid-plan, by running the drift oracle for real (not by the test suite) — this plan's own version of the "run it and see" defects from the section above:
buildstage and zero Task 13 code involved: a.pytest_cache/orrun.logleft behind by Nous's own pre-existing test-running machinery was enough to trigger a false "mechanism drifted" abort — the worst available misdiagnosis, since it reads as a real problem with the mechanism under study. Fixed via two inserted tasks: Task 13.5 adds an opt-inoptimization.build_checks.mechanism_pathsallowlist (default unchanged — the whole-tree behavior — so no existing campaign's recorded hash is invalidated); Task 14.5 closes the follow-on gaps (glob-shaped entries silently half-disabling the allowlist; no--smoke-time check that declared paths actually exist). A further gap (normalization-shaped entries like"src//mech.py", and".."specifically — which makesgitexit non-zero and disarms the drift oracle with zero error anywhere) was closed in Task 14's own scope. One residual (absolute paths) was found and carried forward but is lower-risk — an author is far likelier to write a glob than an absolute path.build-declaring campaign before its one substantive model call ever ran. Task 13's own hoistedbaseline_runscall invoked the target'srun_commandagainst a tree where the mechanism didn't exist yet — verified by direct A/B execution against the pre-fix commit (mechanism authored: 1 before the regression was introduced, 0/crash after, 1 again post-fix). Fixed by degrading gracefully (apre_unavailablemarker) rather than crashing.The final whole-branch review
Per-task review is scoped to one task's diff; it structurally cannot see interactions between an early task's assumption and a late task's different one. After all 18 tasks closed, a dedicated final review traced the compiled state machine end-to-end across the whole ~37k-line diff (109 commits) rather than per-task, specifically hunting for that class of gap. It found and fixed four real cross-task defects:
report.json's recordedpathwas not epoch-scoped — a campaign whose epoch 1 ended in a semantic exception and recompiled into epoch 2 would report apaththat spliced epoch 1's transitions into epoch 2's, naming a state epoch 2 never actually passed through.foldoverescape hatch (optimization.policy.foldover: false) was unreachable from any schema-valid campaign — the schema'sadditionalProperties: falsepolicy block never declared it. Measured the actual cost of the alternative (deleting the dead branch, as an earlier reviewer had recommended) before fixing: on the shipped vLLM example at default resolution,foldoverstays registered-but-never-fired andenumerate_pathsreports 42 reachable-looking paths through it that the campaign can never take — exactly the harm the hatch exists to prevent. Declared the field in the schema instead of deleting the hatch.residual_regret) that meant the model bound at one stage and the terminal bound at another. Renamed toresidual_regret_model(zero external consumers).It also found, and the controller fixed directly, one live correctness gap:
confirmwas the only spending state without anan_response → exceptionrule, unlikescreen/foldover/refine, which each register the identical rule in the identical position. A NaN response atconfirmfell through to confirm's own self-loop default and would run until an outside-the-policy iteration cap cut it off — the one outcome the state machine doesn't register as reachable for a spending state. The fix completes an already-three-times-repeated pattern for the fourth state; mutation-verified (reverting it reproduces the exact self-loop).Three items were investigated and explicitly left open, tracked rather than force-closed:
workload.seed_envawareness. Not unsound (pre and post are symmetric — both seedless — so the comparison stays internally consistent) but CRN's variance cancellation is unavailable in a hard-abort gate whose default tolerance isn't meant to police run-to-run noise. A workload-variance-dominated target combined with abuildstage could false-abort and blame the mechanism. Recommended as a tracked follow-up issue before any real target combinesbuildwithworkload.seed_env.OBSERVATION_KEYShave producers but no consuming rule (mostly deliberate/documented; one,behavioral_violation, is fully orphaned).report.json/recommendation.json/confirmation.json— the "the two regret bounds must never be collapsed into one number" rule currently rests on a single test assertion rather than a schema constraint.The
branch_idfield named in spec §3.9 but never implemented (everytransitions.jsonlrow carriespolicy_hashplus the full fired rule instead) was adjudicated as not a real gap — it appears nowhere in the paper itself, and what's on disk is more informative than a bare id would be. Recorded as a spec amendment, not a code change.Full suite after the final review's fixes: 2009 passed, 1 skipped, 0 xfailed (from 1787 at the start of Phase 1).
tests/test_optimize_harness.py— the file that has carried this plan's oracle gate since Task 3 — has carried zero xfails since Task 11 and still does.Process note
Every task went through the same four-gate pipeline: fresh implementer → independent task review → fix round(s) where warranted → controller-performed audit (scope check, interface check, a mutation the implementer/reviewer hadn't already tried, cumulative full-suite re-verification of every prior task). That audit step caught real issues on its own — including several cases across the plan (Tasks 3, 4, 6, 9) where a controller instruction to an implementer was itself wrong and the implementer's pushback, verified by direct execution, was correct. Two tasks (13.5, 14.5) were inserted mid-plan to close a live production defect discovered organically during implementation, following the same TDD/review/audit process as every planned task. All rulings — every judgment call, every case where a controller instruction was overridden, and their recorded cost-if-wrong — are recorded chronologically in this plan's SDD ledger. Per the workflow, that ledger is deleted at plan completion; the git history, task reports, and this PR description are the durable record.