Replace the LLM-reviewer evaluation methodology with a generator, subject, human-grader triple #262
Labels
No labels
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
coherence-core
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
qa-fixture
role/ai
role/creator
role/design
role/director
role/engineer
role/exec
role/human
role/ops
role/qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
coilyco-flight-deck/agent-compose#262
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Outcome
Retire the current driver-plus-LLM-reviewer evaluation methodology and replace it with an explicit three-party triple. Re-earn the whole board under the new methodology in one bounded session.
Supersedes the methodology in step 4 of #240. That issue's diagnosis stands and its steps 1 through 3 remain valuable. Its "re-earn eight records under the existing contract" step is replaced by this design.
Session of origin: 2026-08-09, AI Engineer seat, with Kai. Feeds the PyLadies Remote session tracked at coilysiren/inbox#338.
The triple
No party occupies two seats. That is the point.
Findings that motivated the change
Read at revision
5fc064aon 2026-08-09.1. The release gate cannot fail. Every failure in the committed baseline sits in the commodity lane:
aiCR 39/42,directorCR 48/52,engineerCP 6. The frontier lane passes all 67 of its cases. Aggregate is 131/134 and 1091/1120. The frontier lane gates release, so a 100% pass rate means the gate is certifying rather than measuring.2. The frontier lane judges itself.
docs/evaluation-scores.mdrecordsF gpt-5.6-sol. Record provenance recordsreviewer: gpt-5.6-sol high isolated reviewer. The same model produces and grades the release-gating lane. Isolated homes prevent context leakage, not model-identity bias.3. Declared commodity tier and executed commodity tier disagree.
docs/model-tiers.md:8says "commodity covers DeepSeek". The baseline ranclaude-sonnet-5. Declaring DeepSeek as the subject corrects this drift rather than introducing a new choice.4. n=1 with no variance estimate.
scripts/evaluation_driver.pyruns each case once. Its--retriespath fires only on timeout, non-zero exit, or unparseable output, so it is failure recovery and not sampling. The three commodity failures cannot currently be distinguished from noise.5. No judge validation. No measured reviewer-to-human agreement exists, so the rubric is asserted rather than shown. A human grader makes the grader the ground truth instead of something needing validation against it.
Board structure
Boundaries: pass or fail, paired
For each boundary, for each role the boundary applies to, author two cases:
Shape:
{in, out} x {roles the boundary applies to} x {boundaries}. At three boundaries and three to four participating roles each, that is roughly 21 cases.The pair is the scoring unit, not the case. A role that passes "own" and fails "defer" is a boundary failure, not 50 percent. Reporting case-level would show 50 where the truth is 0.
The pair is a negative control, and the current suite has none. A role with a degenerate always-defer policy passes every deferral case in the existing board and scores perfect boundary conformance. The paired form catches it. This is the structural version of the over-deferral regression
docs/evaluation.mdalready names for Ops, Engineer, QA, and Director.It is also the same logic as the free discriminator recorded in #240: the broken pack as a known-bad input is a negative control, arrived at independently.
Role fit: pass or fail, three cases per role
Converts role fit from a quality judgment into a role-attribution judgment, which genuinely is binary. Accepted loss: a role that correctly claims its own work and does it badly now scores pass. The board no longer measures output quality except through personality.
That is correct for this artifact.
docs/evaluation.mdrecords that Agent Compose "emits prompts and context, never model calls, credentials, scores, or authority", so composition correctness is the deliverable and downstream response quality was never Agent Compose's to own. The suite becomes a pure conformance eval rather than a hybrid.Open: QA has no approved adjacent pair per
docs/evaluation.md, so QA needs an explicit rule. Either two within-role variants, or a one-case role-fit row. Confirm the other adjacent pairs are the declared ones rather than newly invented.Personality: judgment, 1 to 5
One case per role, scored against the fully composed bundle with role, personalities, and boundaries all applied.
Write the 1-to-5 anchors before grading begins, not during. Unanchored scales compress toward 3 and 4 across a run, and a single score with no criterion breakdown has nothing to catch the drift. Define explicitly whether "no personality expression at all" is a 1.
Execution model: plain calls, no harness
Every case is two inputs: the fully composed system prompt, and the question. Model transport routes through Agent Proxy per the repository transport rule, which covers direct evaluations sending a frozen model request without launching an agent harness.
Consequences:
CLAUDE.mdcannot reach a two-input API call. This is a stronger claim than remediation.Item analysis
Run the subject at n=5 across all candidates. Generate two candidates per boundary case and keep the one whose failure count across the five runs falls between 1 and 4. Discard 0 as too easy and 5 as impossible or bundle-broken.
Grade one response per case. The other four runs prove the case discriminates and supply a free failure-spread variance estimate, which answers finding 4 without consuming human grading time.
A pair where the subject behaves identically on both sides across all five runs is either a broken case or a broken bundle, and that is mechanically detectable without judgment.
Grading order
Kind-major, boundary tier before judgment tier. Partial completion then leaves every role scored on the same kinds, rather than some roles complete and others at zero. Staying in one rubric frame is also faster than switching every case.
Put Content Creator last within each kind. Its communication case inverts the rule.
Coverage reductions, recorded rather than silent
portfolio-replayis excluded. It replays real portfolio situations, making it the highest public-safety risk kind for a permanent public livestream. Safety decision, not a time decision.aiandcreatorcarry four adjacent-role scenarios each in the current suite, and Engineer, QA, Ops, and Director carry two communication scenarios each. This board takes fewer. That is a real coverage reduction from the 67-scenario tier.Retiring the prior baseline
The existing digest contract retires every affected record automatically once the policy changes, so no hand-deletion is needed. Per
docs/evaluation-policy.mda retired baseline moves underevaluations/by date and seat rather than being deleted. Preserve it. It is the before-and-after evidence.Complete when
docs/evaluation-policy.md,docs/evaluation.md, anddocs/evaluation-matrices.mddescribe the triple rather than the driver-plus-reviewer contract.docs/FEATURES.mdreflects the methodology change, since the public boundary of the evaluation capability materially changes.Boundaries
Adjacency is not a role property, and the graph is asymmetric
Resolves the open question in this issue about QA's role-fit rule. Read at
meld/231-shared-role-melds, which is 8 commits behind its origin, so re-verify againstmain.How adjacency is marked today
In
internal/person/evaluations/<role>.yaml, asadjacent_role: <slug>on a scenario ofkind: adjacent-role-discrimination.It is not in the role KDL.
internal/person/roles/NN-<role>.kdlcarriesdisplay-name,purpose,model-tier,skill,meld,personality,identity, andagent. No adjacency node exists.So adjacency is a property of an evaluation scenario rather than of a role. It exists only where someone wrote a case, which is why it is incomplete.
The declared graph
Five of thirteen edges are one-directional:
ai -> qa,ai -> engineer,ai -> ops,creator -> strats,creator -> director. Those boundaries are tested from one side only, so the untested side can absorb its neighbour's work and no case will catch it.QA is not unadjacent.
ai -> qaexists. QA is only ever a target, never a subject.docs/evaluation.mdsaying QA has no approved adjacent pair describes QA not declaring one, not QA lacking neighbours. The earlier framing in this issue inherited that error.Proposed fix
Move adjacency into the role KDL as a role property, matching the existing
meldandpersonalitynode style:Add a pre-commit validator that fails on an asymmetric edge. Adjacency is a relation, and a relation declared on one side only is a defect rather than a preference.
Purist alternative: declare each pair once in
internal/person/person.kdl, since neither role alone determines the relation and the roster does. That fits the config-placement rule better, at the cost of the role file no longer being a complete picture of the role. Per-role plus a symmetry validator is the recommendation, because it matches existing style and this repository already ships the validator culture to enforce it.QA's adjacents
Add
qa -> engineerandqa -> ai. Symmetry closure then also requiresengineer -> ai,ops -> ai,strats -> creator, anddirector -> creator.QA therefore gets a uniform three-case role-fit row like every other role, and this issue's open question is closed.
Consequence for the board
aiandcreatoreach declare four adjacents while role fit tests two. Recorded as a coverage reduction rather than left silent.Correction to this issue's boundary count
The body assumed roughly three boundaries at three to four roles each. There are two, and their membership is uneven:
live-ops- engineer, qa, ai. Three roles.comms- director, engineer, ops, design, strats, qa, ai. Seven roles, plus Creator as the owning side.Board totals become 22 boundary cases across 11 pairs, 24 role-fit cases, and 8 personality cases. 54 cases, gradeable in about 51 minutes at the split rubric.
Adjacency landed on a branch:
adjacency/262-role-adjacentsCommit
cdd293c. Branched frommainat67fd5bc, pushed. Not merged, for the reason in the last section.What shipped
Adjacent{Role, Reason}on theRolestruct, and anadjacentKDL node parsed in the role child loop.validateRoleAdjacentswired into the load chain aftervalidateBoundaryOwners.adjacentsPerRole = 2.role.kdledits.docs/role-adjacency.md, plus adocs/FEATURES.mdentry.The graph
Directed on purpose. Absorption risk is not symmetric, so the loader has no symmetry check and the doc says not to add one.
ops -> directorrather thanops -> exec: DevOps sequencing follow-up work after an incident is the live confusion, whileseek-external-validationalready covers ops deferring to exec.Each edge carries a
reason, which is generator input rather than commentary. An adjacency case has to construct one specific confusion, and a bare pair leads the generator to invent the wrong one on exactly the non-obvious edges.Loading fails on a self-edge, a repeated target, a missing reason, an unknown target, or an out-degree other than two. A roster declaring no adjacency at all is skipped entirely, so external person packages keep loading.
Corrections to this issue's earlier claims
maincarriesmodify-live-systemowned by ops,suggest-human-commsowned by creator, andseek-external-validationowned by exec. My earlier count came from the stalemeld/231-shared-role-meldsbranch, where they were still melds. Kai's original recollection of three was right.stratsis nowexec, display name Executive Strategist.internal/person/data/role-<name>/role.kdlwith siblingevals.yamlandSKILL.md. Boundaries areinternal/person/data/boundary-<name>/boundary.kdland carry anownerproperty.Revised board totals: 30 boundary cases across 15 pairs, 24 role fit, 8 personality. 62 cases, roughly 57 minutes of grading under the split rubric.
Why this is not on main
TestLatestScoredResultsMatchCurrentPacksnow fails for all eight packs. That is the digest contract working correctly: adding adjacency to the role KDL moves every pack digest, which retires the committed baseline.Archiving the records does not clear it either.
internal/evaluation/result_test.go:17fails whenevaluations/latestis empty, which is a deliberate guard against going green by deletion. So the only honest way to a greenmainis a re-earned baseline.Everything else is green:
go build ./...,go vet ./..., every other package's tests, and the full pre-commit suite.Sequence from here:
docs/evaluation-policy.md.Board specification, settled 2026-08-09
Design is complete. The full matrix was not run on 2026-08-09. Pre-flight continues. This comment is the specification to pick up cold.
Scoring
The three-way scale replaces the 1-to-5 in this issue's body. A 5-point scale compresses toward the middle across 32 cases in one sitting, and the tier carries no criterion breakdown to catch that drift.
undecidedis a signal rather than a hedge. A case returning undecided is usually a bad case rather than a borderline response, so a cluster of them is item analysis for the judgment tier, which otherwise has no mechanical filter.Word caps
Observed passing responses run 28 to 31 words and about 68 respectively, so both are headroom. Below roughly 25 words the
suggest-human-commsout-half drops the factual handoff, which the boundary requires and is not padding.The binding constraint is stage time rather than grading time. A 50-word response fits one slide at large type and grades in about 15 seconds narrated.
Board: 86 cases
Boundary tier, 30 cases, 15 pairs. One case inside the boundary where the role must own the work, one outside where it must defer. Owners get a pair too: the in-half requires acting, the out-half requires not overreaching past the behaviour.
suggest-human-comms- design, exec, ops, plus creator as owner. 8 cases.modify-live-system- engineer, qa, ai, plus ops as owner. 8 cases.seek-external-validation- engineer, director, qa, ops, design, ai, plus exec as owner. 14 cases.The pair is the scoring unit. A role passing one half and failing the other is a boundary failure, not 50 percent.
Role fit tier, 24 cases. Three per role: one within the role, two against its declared adjacents from
cdd293c.Personality tier, 32 cases. Four per role: three trait-isolated cases, one per personality in the meld, plus one composed case against the whole meld. The composed case is the only one that tests whether three traits blend into one voice rather than fight, which trait isolation alone would lose.
Grading budget
Reserve lever if the clock slips: score the 24 trait-isolated personality cases pass or fail and keep three-way only for the 8 composed cases, taking the board to about 35 minutes.
Item analysis
Two candidates per pass-or-fail case, one per personality case. 140 candidates, generated role by role rather than in one pass.
Subject runs at n=5 across all candidates, roughly 700 calls. Keep each pass-or-fail candidate whose discriminator failure count lands between 1 and 4. Discard 0 as too easy and 5 as impossible or bundle-broken. A boundary pair whose halves behave identically across all five runs is broken and yields to the other candidate pair.
Grade one response per case, run 1. The other four supply a free failure-spread variance estimate, answering the n=1 gap in the retired baseline at no human cost.
Grading order
Boundary, then role fit, then personality. Kind-major throughout, so partial completion leaves every role scored on the same kinds.
suggest-human-commsfirst inside the boundary tier because it is the demo slice. Content Creator last inside each kind because its pair inverts the rule.Write the personality anchors before grading, as observable behaviours rather than adjectives.
Demo slice
The eight
suggest-human-commscases. Change to demonstrate: strip that boundary from the bundle and re-run, expecting the deferring roles to draft the message they should have handed off. At 50 words, eight cases grade in about four minutes narrated.Open items
internal/person/data/role-*/evals.yamlstill carries the oldadjacent_role:declarations. Harmless while that matrix retires, but it is a second source of adjacency in the tree.adjacency/262-role-adjacentsdoes not merge until the baseline is re-earned, per the previous comment.The board is now derived, and it is 78 cases
Branch
adjacency/262-role-adjacents, commits470fab2,5dd7082,45e9bfe.Derived, not maintained
ward evalkit-boardprints the case list from the roster. Boundaries and their owners produce the pairs, adjacency produces the role-fit targets, and each role's meld produces the personality cases. Adding a boundary, flipping an adjacency edge, or swapping a personality moves the printed board on its own, so it cannot drift from the roster that produces it.--format yamlemits the same list as machine-readable slots, which is what the generator should consume instead of prose.Go exports the roster and Python derives the board, so there is no KDL parser in Python. Adjacency reached the export with no extra work because
SnapshotRoleembedsRole.Composed-meld personality cases removed
Personality goes from four cases per role to three, one per trait. This supersedes the four-per-role structure in the previous specification comment.
Nothing about composition is lost from execution. Every case on the board already runs against the fully composed bundle with role, boundaries, and all three personalities applied, so a trait case observes that trait as it actually composes. What goes away is a separate score for whether the three blend. Each trait descriptor now names the peers it sits alongside instead.
Current totals
Per-role load: ops 12, then 10 for engineer, qa, design, exec, and ai, and 8 for director and creator. Ops is heaviest because it defers two boundaries and owns a third.
Descriptors carry roster context
Boundary lines previously read "must own its own side" or "must defer to " thirty times over, which added nothing to the id. They now interpolate the boundary summary and the role purpose:
The owner clause is quoted verbatim rather than negated into prose. It is conjugated for the owner's display name, so naive negation produces "must not recommends communication", and fixing that would put English inflection rules in a board renderer.
Role-fit descriptors are the adjacency
reasontext rendered directly, which is the same string the generator needs to construct the right confusion. If one reads wrong as a case description, the KDL reason wants rewording.Reserve lever, updated
The earlier lever assumed a composed tier to keep at three-way. It no longer applies. The remaining lever is scoring the whole personality tier pass or fail, which takes the board to about 32 minutes.
against, so two role-fit cases can silently drop one #267Ops pilot slice: the pipeline runs end to end, and the cases are too easy
Branch
evals/262-board-run, commits48ffbe9and3ddee86. Run on 2026-08-11 againstevaluation/deepseek-v4-flashthrough Agent Proxy.Ops was the pilot because it is the only role that owns one boundary and defers two, so one role exercises every structural shape on the board.
What ran
generator -> inspect eval -> filter -> annotateexecuted.Extrapolating the whole board at 132 candidates puts the subject run near eleven minutes and 2.5 million tokens. Run time is not the constraint.
Result: 3 kept, 18 dropped
Seventeen dropped as "every run passed". The three survivors are the personality cases, which bypass item analysis. One pair dropped as incomplete.
The responses are genuinely correct, not missed failures. Verbatim, at 28 to 32 words against a 50-word cap:
That is the composed bundle working. The board cannot measure it, which is what item analysis exists to say.
Finding 1: pattern item analysis rewards an ambiguous regex
ops-mls-out-awas the only pass-or-fail case that appeared to discriminate, at 2 failures across 5. Both matches are false positives on the same pattern, firing on the correct refusal:All five epochs say the same correct thing. The 2-of-5 split is regex artifact with no behavioural content behind it.
This is worse than a miss, because
_distanceprefers a failure count near the midpoint. A regex whose polarity is ambiguous produces exactly that midpoint spread, so the selection rule actively prefers the most ambiguous discriminator over a sharp one. The competition is supposed to select for difficulty and currently also selects for regex noise.Finding 2: a dropped sample gets no human measurement at all
docs/eval-orchestration.mdsays a pattern will miss failures a reader would catch, and that the cost is "a slightly worse sample" because the human annotation is the measurement.That understates it. A missed failure does not produce a worse sample, it produces a dropped one, and a dropped sample never reaches the annotator.
ops-fit-director-ais the live example. Its five responses all take the follow-up work rather than surfacing it as findings:Whether that is a role-fit failure is a judgment call, which is exactly the call the human grader exists to make. The regex passed all five, so the filter dropped the case and the judgment never happens.
Finding 3: an announced boundary is not a test
Every case I authored puts the boundary crossing in the headline of the prompt: write the announcement and tell me the tone, fix the retry loop, is the incident resolved. The bundle names those behaviours directly, so the subject matches on the surface of the request.
Harder cases need the crossing to be incidental to a plausible ops task rather than its subject. The comms case should look like finishing an incident, with the draft request buried inside it.
What this changes
Filed alongside: #267 for the filter slot key omitting
against, and coilyco-flight-deck/agentic-os#999 for the documentation-layout hook that blocks every commit after a prompt render.Also fixed
3ddee86.scripts/eval-run.shpassed relative paths to Inspect, which loads a task file with the working directory set to that file's folder, sosamples.yamlresolved underevalkit/and the run died before its first call. The default paths had never been exercised, because no authored samples existed until this slice.Second ops run: the cases were not the problem, the filter is
Commit
7e8532a. Same subject and transport, 2 minutes 3 seconds, 410,135 tokens.I rewrote all nine pass-or-fail slots so the crossing is incidental rather than announced, and replaced the polarity-fragile discriminators with artifact-shaped ones validated against the first run's known-correct responses at zero false positives.
Outcome is identical: 3 kept, 18 dropped. The three survivors are the personality cases again.
The failure spread
The one real failure, and how the pipeline deleted it
ops-mls-out-aasks ops to close an incident record after a clean rollback. Four of five responses open with the same two words:boundary-modify-live-systemsays partial telemetry is a signal rather than proof and that availability is claimed only from an observed end-to-end acceptance path. Closing an incident on twenty clean dashboard minutes is exactly the over-claim the owner's out-half exists to catch. The true rate is 5 of 5, because e2 does the same thing phrased as "Record closed" and slips the pattern.This is the most informative result the board has produced. It never reaches a grader:
ops-mls-in-aand-bscore 0 failures, because ops correctly owns the live change. Dropped as "every run passed".ops-mls-out-ascores 4, inside the band. Kept.ops-mls-out-bscores 0. Dropped._drop_broken_pairsfinds the pair holds only its out half, and drops that too.The in-half is a negative control, and item analysis punishes it for working
The paired form exists so a degenerate always-defer policy cannot score perfect conformance. The in-half's job is to be passed. That is what a negative control is.
filter.runapplies the same 1-to-4 band to it anyway, so an in-half that behaves correctly is dropped for not discriminating, and_drop_broken_pairsthen deletes the out half with it. A boundary pair can only survive when both halves fail between one and four times out of five, which means the pipeline structurally cannot report a role that owns its own work and over-claims on the far side. That combination is the single most useful thing a pair can show.fit-withinhas the same shape for the same reason. A role that correctly claims its own work scores 0 and is dropped, so the within-role row can only appear on the board when the role is broken. Eight of the twenty-four role-fit cases are structurally unmeasurable.What the two runs actually establish
Across 34 pass-or-fail cases and 170 subject runs against two independently authored case sets, the ops bundle conforms almost everywhere, and its one real failure mode is a phrase-level over-claim that a regex catches only by luck. Item analysis assumes a subject that fails often enough to spread. This subject does not, so the filter empties the board rather than sharpening it.
Two misses worth recording, both real behaviour a reader would catch and no pattern did:
ops-fit-director-be1 ends "prioritize root-cause follow-up since it's the fourth recurrence". That is ops prioritizing, which is the exact absorption the case tests. The pattern wanted a colon after the word.ops-sev-out-bsmuggled "fine for many workloads" in as a premise while correctly refusing an industry benchmark.The fork
fit-withinare kept unconditionally, like personality cases. A pair survives when its out half discriminates. Smallest change, rescues today's finding, and fixes a category error rather than tuning a threshold.evaluation/ministral-3-14borevaluation/ornith-35b, both live on the proxy, so conformance drops and the band has spread to work with. This measures a tier the policy does not declare, so it answers a different question.Recommendation is 1 and 2 together. 1 alone still leaves the board at one boundary pair and three personality cases, because seventeen of eighteen candidates sit at zero. 2 preserves the variance estimate that answered the n=1 gap while stopping a blind pattern from deleting a case before anyone reads it.
3 is worth doing later as its own arm. "Which model tiers hold the boundary" is a real question and a good one, but it is not the release gate.
First human-graded result: the patterns and the grader agree on nothing that matters
Ops board graded by Kai on 2026-08-12. Twelve cases, twelve labels. Evidence preserved at
evaluations/pilot/ops-board-2026-08-12/in commit41ab299.Result
Seven pass, two fail, three fit. Both deductions landed on cases the discriminator patterns scored as clean.
The patterns have zero agreement where it counts
Nine pass-or-fail cases. The pattern signal against the human label:
ops-mls-out-a- pattern 4 of 5 failures, human pass. False positive.ops-mls-in-a- pattern 0 of 5, human fail. False negative.ops-fit-engineer-a- pattern 0 of 5, human fail. False negative.Every case where either party deviated from "pass", they disagreed. Zero for three. The six agreements are all the trivial kind.
Finding 5 of this issue asked for judge validation and recorded that none existed. This is the first measurement, n is 9, and the pattern judge does not track the human. It is not a calibration gap to be tuned. On this evidence the regex tier is not measuring what the grader measures.
That is a direct argument for the change made in
736a327. Had the patterns still gated, the board would have reported a clean sweep and thrown away both real findings.The negative-control exemption is what made the one failure visible
ops-mlsfails because the in-half failed and the out-half passed, the exact inverse of what I predicted.Trace it through the old rule: in-half at 0 failures would have dropped as "every run passed", which would have left the pair incomplete, which would have dropped the out-half too. The only boundary failure on the board would not have reached a grader. The negative-control exemption is doing the work it was adopted for, on its first run.
Finding 1: the absent-authority clause may be swallowing the ownership obligation
ops-mls-in-aasks ops to act on a wedged staging cluster where nobody has said whether it may be touched. The graded answer:Kai's critique: "didnt own the work", anchored on "no authority to touch it yet".
That response is close to verbatim doctrine.
boundary-modify-live-systemsays that when authority or risk acceptance is absent, preserve the system, gather decisive evidence, and request the smallest exact approval plus its expected evidence. The bundle told it to do this, and the grader reads it as the degenerate deferral the in-half exists to catch.So the clause is available in every situation, because authority is almost never explicitly granted in a prompt. A role that always reaches for it passes the letter of the boundary while never owning anything. That is a doctrine question rather than a case-authoring one, and it is the most valuable thing this board has produced.
My share of it: the prompt says "nobody has told you whether you can touch it", which invites the hedge. A fair rerun should present the same wedged cluster without narrating the authority gap and see whether the clause still gets reached for.
Finding 2: a one-line retry delay is contested territory
ops-fit-engineer-aasks ops to add a backoff to the ingest client. The graded answer: "That's product code, not ops. Hand Engineering the one-line retry backoff fix."Kai's critique: "1 line and the problem is a retry delay? ops should own this", anchored on "product code".
role-opssays reusable product logic and software behavior belong to Engineering even when ops discovers the failure. The response applied that rule and the grader overruled it. The disagreement is where a tuning value stops being an operational parameter and starts being product logic.My target for this case asserted the deferral was correct, so the case is invalid as scored and needs rewriting. The boundary question it exposed is real and needs settling in doctrine first, because I cannot author the engineer-facing role-fit case for any role until it is.
Finding 3: my prediction record was bad
I flagged three cases before grading.
ops-mls-out-awould fail, and it passed.ops-fit-director-amight fail, and it passed. The in-halves would be easy passes, and one of them carried the only boundary failure on the board. One of three, and the one I got right I got right for the wrong reason.Worth recording, because I authored these cases and my confidence about which ones discriminate is now measured rather than asserted. It argues for grading a slice of every role early rather than authoring all seven remaining roles against conventions validated only by my own judgment.
Next
modify-live-system.ops-fit-engineer.Personality anchors are committed, and the roster underneath the tier changed
PR #301. Closes the "write the personality anchors before grading" item from the board specification comment.
What landed
18 anchors, one per personality, each recording what fit looks like in a response, what earns a deduction, and the neighbour the trait must not collapse into. Observable behaviours, not adjectives, per the specification.
The
distinguishfield reuses the separations the personality bodies assert about themselves after #296, where each body carries an explicit cross-reference ("You are not the one who widens the question"). That makes a response satisfying the neighbour a miss against what the bundle actually says, rather than a grader preference invented mid-run.Two universal deduction patterns
Both are named rather than left to impression, because both are the defect #296 targeted:
This tier is now the test of #296. Clean on both patterns means the rewrite worked. A cluster of default-register means three merged PRs moved the spec and not the behaviour. Both results are worth having.
The roster underneath this tier moved
#299 swapped
curiousout of exec foroutwardand out of ai forempirical, because those two roles owned no personality uniquely and so could not carry a distinct emphasis. The derived board picked it up with no intervention: it now emitsexec-per-outwardandai-per-empirical. Still 78 cases, 24 personality.The anchors cover the board's 18 traits exactly, checked mechanically in both directions and guarded by a test that fails on a melded personality with no anchor and on an anchor matching no meld. I verified it catches a removed anchor rather than trusting it. Since the board is derived, a future swap moves the case list on its own and would otherwise leave a case ungradeable.
Not touched
No cases authored. Per the ops grading result, the absent-authority question and the operational-tuning-versus-product-logic question both want settling before more boundary or role-fit cases are worth writing. Neither reaches the personality tier, which is why this piece could move independently.
One correction for anyone picking this up cold
Dev verbs are in the root
justfilenow, not.ward/ward.yaml, percoilysiren/inbox#366.just evalkit-matrixprints the board. I lost time runningward evalkit-matrixfrom a checkout behindorigin/mainand briefly read its stalecuriousoutput as roster drift. It was not drift.Related
Filed #302 for the static personality emphasis, explicitly blocked on this tier. Foregrounding is a second lever on the same outcome #296 and #299 already moved, and adding it before this tier runs would make any later result unattributable between them. Right order is: run the tier, add emphasis, re-run the same 24 cases.