Development record
An ideate verdict proves its own legs
Press Release
An ideate verdict proves every leg it records, not just the record grill. Every record id in every leg resolves, and a primary source must be a web address, a filed source or a record. The grill says what it searched and where, so an empty grill can be told from a broken search. A reframing has its own field. Two reviewers are recorded reviewer by reviewer, and a split between them carries the ruling that settled it. A claim checked only against the idea's own source reads "quoted", never "verified".
"I read a verdict that killed an idea," said Bob, a staff engineer. "I could see which searches came back empty, that both reviewers agreed, and that the claim it leaned on was only checked as quoted. I trusted the kill because I could check it."
Why This Matters
abcd ideate record checks citations only in the grill leg. A lab hand-run of
2026-10-14 recorded, with exit 0, a verdict that cited a non-existent intent in
leg 3 and another in leg 1 and named "a remark heard in passing" as a verified
primary source (iss-2610141647304982). The same lab found that a broken grill
and an empty one write the same record (iss-2610141647307807); that two
reviewers and a disagreement between them have no place in the record
(iss-2610141647308358, iss-2610141647315880); and that "verified" cannot say
"verified as quoted" (iss-2610141647313018). Each is a missing field or a
missing check, and the binary already checks the grill leg's ids, so the same
kind of check extends to the rest.
Breaking, for anything that records a verdict: a payload with an unresolved id in any leg, a free-text primary source, a reframed verdict without its reframing, or schema version 1 is refused where it was accepted.
What's In Scope
- Schema version 2 of the ideate verdict, its validation, and its rendering.
- The command page
commands/ideate.mdupdated to the new shape in the same change.
What's Out of Scope
- Private verdicts and citing them (itd-2610141646456128).
- Ideate starting from a source, and citing pack items (itd-2610141646452213).
- Judging any claim: the binary checks shapes and citations, never the truth of a claim.
Mechanism
We expect no verdict recorded after this change to carry an unresolved id, an unlabelled self-quotation or an unrecorded split, because each is refused at record time by a check on the payload's shape and citations. Shown wrong if a version 2 verdict on record carries an id that does not resolve, or a claim marked verified whose only primary source is one of the idea's own origin sources.
Scope Conditions
- The writer accepts version 2 only; a version 1 payload is refused with a message naming version 2. Verdicts already on record are rendered Markdown and are not parsed or backfilled.
- The command page and the binary ship together; a host reading an older page gets the version-1 refusal message.
- The binary checks shapes and citations. Whether a claim is true, or how a reviewer reached a verdict, is the host's and the person's judgement.
Acceptance Criteria
- Given a verdict citing a record id that does not resolve, in any leg, when it is recorded, then it is refused, naming the leg and the id.
- Given a claim whose primary source is not a web address, a filed source key or a record id, when the verdict is recorded, then it is refused.
- Given a grill leg with no hits and no search record (the terms searched, the places searched, and the count each returned), when the verdict is recorded, then it is refused.
- Given a verdict marked reframed with no reframing field, when it is recorded, then it is refused.
- Given two reviewers, when the verdict is recorded, then each reviewer's kill attempts, verdict and consulted sources are kept under that reviewer.
- Given two reviewers whose verdicts differ, when the verdict is recorded without a ruling (who ruled, and the ruling), then it is refused.
- Given a claim marked verified whose only primary source is listed among the verdict's origin sources, when it is recorded, then it is refused; marked quoted, it is accepted.
- Given a version 1 payload, when it is recorded, then it is refused with a message naming version 2.
- Given the updated command page, when it is read, then it tells the runner to give each reviewer one idea per context.
Prior Art
- itd-104 (shipped): the ideate admission gauntlet and
ideate record, whose grill-leg citation check this extends to every leg. - itd-1 (discipline): every audit verifies acceptance criteria; the verdict's own legs get the same treatment.
- iss-2608230748418054 (open): the grill's structural blind spot over the research notes, which the search record makes visible.
SOTA
- Structured-output validation of model-produced records (JSON Schema validators, Pydantic-style models): mature; validates shape, not truth.
- Citation resolution in research tooling (DOI and URL resolvers): mature for external references; abcd's record ids need abcd's own resolver.
Path: 2. A native floor: abcd's own resolver and schema check, since the ids are abcd's own; an external validator adds nothing the Go check lacks.
Open Questions
None.
Audit Notes
Fidelity review — receipt rcp-dbe525f06247 (verifier intent-auditor claude-opus-5-5).
Provenance: intent-auditor@claude-opus-5-5 · rubric_hash sha256:effa65b3e9e88ff29433b443ec2be159522a8b0b71cf1434526514aa61edb13e · prompt_hash sha256:4685a5a288619851380647e707ec06360e84954083144001c915d57ba4776be2 Input attestations: diff:c66f9959f..9c77aae4f@sha256:4b98c145c87cfc93bf26e7c5a45b7d7c43d64d5cb7016abb7a79d01464953c07; diff:9c77aae4f..a5629f871@sha256:dc5d3e380f731baeca83aea1099ffd186462719a598cd33558337d9eb1a0b0fb;
Acceptance rollup: MET 9 · MET_WITH_CONCERNS 0 · NOT_MET 0 · INCONCLUSIVE 0
Per-criterion verdicts:
- ac-1 — MET: resolveCitations scans claims, primary sources, grill notes, kill attempts, reviewer sources, rejected alternatives, origins and the reframing, and the fix 9c77aae4f..a5629f871 adds reviewer labels and the ruling's 'by' beside its reason; CitationError names each part and id, tested per field including the two new cases. The grill's search terms and places stay unchecked by documented design: a search term records what was searched for, not a citation, so it falls outside 'citing a record id' evidence: ac1-fix.diff:35 — "+ add(adversarial, r.Label)" evidence: ac1-fix.diff:43 — "+ add(adversarial, v.ruling.By, v.ruling.Reason)" evidence: ac1-fix.diff:55 — ""reviewer label": {func(p map[string]any) { reviewer(p, 0)["label"] = "the reviewer of an unfiled id" }, "adversarial-review", "an unfiled id"}," evidence: ac1-fix.diff:57 — "leg(p, 2)["ruling"] = map[string]any{"by": "the advisor named in an unfiled id", "verdict": "survives", "reason": "both agree"}" evidence: internal/core/ideate/record.go:1006 — "for _, c := range v.claims { add(research, c.Claim, c.PrimarySource)" evidence: internal/core/ideate/record.go:100 — "parts[i] = c.Where + " cites " + c.ID" evidence: internal/core/ideate/record.go:990 — "nor are the grill's search // terms and places, where a searched-for id that names nothing is the finding." evidence: internal/core/ideate/v2_test.go:38 — "func TestV2RefusesAnUnresolvedIDInAnyLeg(t *testing.T) {"
- ac-2 — MET: validateResearch refuses a primary source that sourceShaped rejects; sourceShaped accepts only an https:// address with a host, source:< key> in the corpus key grammar, or a record id, and a key is resolved against the default corpus when one exists; tested with 'a remark heard in passing', file://, http:// and malformed keys evidence: internal/core/ideate/record.go:572 — "if !sourceShaped(src) {" evidence: internal/core/ideate/record.go:600 — "func sourceShaped(src string) bool {" evidence: internal/core/ideate/record.go:1194 — "the verdict cites sources the sources corpus does not carry — nothing was written" evidence: internal/core/ideate/v2_test.go:96 — "func TestV2RefusesAPrimarySourceOfNoKnownShape(t *testing.T) {" evidence: internal/surface/cli/ideate.go:190 — "if dir, err := source.DefaultDir(); err == nil {"
- ac-3 — MET: validateGrill refuses no hits together with no searches, and validateSearches requires each search's terms, places and a non-nil, non-negative count; tested, including the accepted empty grill with a recorded search evidence: internal/core/ideate/record.go:685 — "if len(leg.Hits) == 0 && len(leg.Searches) == 0 {" evidence: internal/core/ideate/record.go:738 — "if s.Count == nil {" evidence: internal/core/ideate/v2_test.go:188 — "func TestV2RefusesAGrillWithNoHitsAndNoSearches(t *testing.T) {"
- ac-4 — MET: validateReframing refuses a reframed verdict whose cleaned reframing is empty (absent or blank), and refuses a reframing beside any other verdict; tested both ways evidence: internal/core/ideate/record.go:970 — "case p.Verdict == VerdictReframed && reframing == "":" evidence: internal/core/ideate/v2_test.go:239 — "func TestV2RefusesAReframedVerdictWithoutAReframing(t *testing.T) {"
- ac-5 — MET: the Reviewer type holds label, kill_attempts, verdict and sources; Leg carries no top-level kill attempts; the renderer writes one '### Reviewer < label> — < verdict>' section holding that reviewer's attempts and sources; the test asserts each reviewer's attempt and source appear only in its own section evidence: internal/core/ideate/ideate.go:296 — "type Reviewer struct {" evidence: internal/core/ideate/render.go:134 — "fmt.Fprintf(b, "### Reviewer %s — %s\n\n", r.Label, r.Verdict)" evidence: internal/core/ideate/v2_test.go:277 — "func TestV2KeepsEachReviewerUnderItsLabel(t *testing.T) {"
- ac-6 — MET: validateRuling refuses differing reviewer verdicts with no ruling, and a given ruling must name who ruled, a valid verdict and a reason; tested, including the rendered ruling evidence: internal/core/ideate/record.go:880 — "if in == nil { for _, r := range reviewers[1:] { if r.Verdict != reviewers[0].Verdict {" evidence: internal/core/ideate/record.go:898 — "case out.By == "":" evidence: internal/core/ideate/v2_test.go:321 — "func TestV2RefusesASplitWithoutARuling(t *testing.T) {"
- ac-7 — MET: validateResearch refuses a verified claim whose normalised primary source is among the origin keys, and quoted is a registered status; tests cover alternate spellings, an origin with surrounding words, an omitted origin_sources list (refused) and an accepted quoted claim evidence: internal/core/ideate/record.go:580 — "if c.Status == ClaimVerified && origin[sourceKey(src)] {" evidence: internal/core/ideate/ideate.go:200 — "ClaimQuoted ClaimStatus = "quoted"" evidence: internal/core/ideate/v2_test.go:367 — "func TestV2RefusesAVerifiedSelfQuotation(t *testing.T) {"
- ac-8 — MET: SchemaVersion is 2; decodePayload peeks schema_version before the strict decode and refuses an older version with olderSchemaError, which names version 2; tested in the core on a real v1-shaped document and at the CLI (exit 2, 'version 2') evidence: internal/core/ideate/ideate.go:34 — "const SchemaVersion = 2" evidence: internal/core/ideate/record.go:377 — "if json.Unmarshal(raw, &peek) == nil && peek.SchemaVersion > 0 && peek.SchemaVersion < SchemaVersion {" evidence: internal/core/ideate/record.go:413 — "compose it as version %d, which adds origin_sources" evidence: internal/core/ideate/v2_test.go:447 — "func TestV2RefusesAVersionOnePayload(t *testing.T) {" evidence: internal/surface/cli/ideate_surface_test.go:158 — ""version-1-payload","
- ac-9 — MET: the command page's leg 3 lists 'One idea per context' among the leg's properties, and a test reads the leg 3 section of the page for the phrase evidence: commands/ideate.md:128 — "- One idea per context. Give each reviewer exactly one idea in its context," evidence: internal/core/ideate/v2_test.go:488 — "func TestCommandPageGivesEachReviewerOneIdeaPerContext(t *testing.T) {"
Gap audit:
- honoured:
- a primary source must be a web address, a filed source or a record evidence: internal/core/ideate/record.go:600 — "func sourceShaped(src string) bool {"
- the grill says what it searched and where, so an empty grill can be told from a broken search evidence: internal/core/ideate/record.go:685 — "if len(leg.Hits) == 0 && len(leg.Searches) == 0 {" evidence: internal/core/ideate/render.go:115 — "b.WriteString("| Terms | Places | Count |\n|---|---|---|\n")"
- a reframing has its own field
evidence: internal/core/ideate/ideate.go:349 — "Reframing string
json:"reframing,omitempty"" evidence: internal/core/ideate/render.go:186 — "b.WriteString("## The reframing\n\n")" - two reviewers are recorded reviewer by reviewer, and a split carries the ruling that settled it evidence: internal/core/ideate/render.go:134 — "fmt.Fprintf(b, "### Reviewer %s — %s\n\n", r.Label, r.Verdict)" evidence: internal/core/ideate/record.go:883 — "the reviewers' verdicts differ (%s) and the adversarial-review leg records no ruling — nothing was written"
- a claim checked only against the idea's own source reads quoted, never verified evidence: internal/core/ideate/record.go:580 — "if c.Status == ClaimVerified && origin[sourceKey(src)] {"
- the command page is updated to the version 2 shape in the same change, and its example decodes as this binary's payload evidence: commands/ideate.md:154 — ""schema_version": 2," evidence: internal/core/ideate/v2_test.go:510 — "if err := dec.Decode(&p); err != nil {"
- diverged:
- every record id in every leg resolves: delivered over every prose, label and source field (reviewer labels and the ruling's 'by' added in a5629f871), with the grill's search terms and places exempt by design, as the brief, the command page and the code comment all state evidence: internal/core/ideate/record.go:994 — "func citationPlaces(v verdictDoc) []citedText {" evidence: ac1-fix.diff:25 — "part of the address). A search term naming an id is not checked, because" evidence: .abcd/development/brief/04-surfaces/18-ideate.md:151 — "grill's search terms and places are not checked, because a searched-for id"
- a primary source may be a web address: delivered as https:// only, so an http:// address is refused evidence: internal/core/ideate/record.go:602 — "case strings.HasPrefix(src, "https://"):" evidence: internal/core/ideate/v2_test.go:100 — ""http://example.invalid/plain","
- a filed source key is proved filed: only where a sources corpus exists; with none, a well-formed key is accepted on its shape alone evidence: internal/core/ideate/record.go:1174 — "case errors.Is(err, source.ErrNoCorpus): return nil"
- missing: (none)
Scope-condition dispositions:
- cond-2610142138048878 — survived: the writer refuses every version below 2 with a message naming version 2, and the ideate package has no non-test file read: existing verdict records are only ever created exclusively, never parsed or backfilled evidence: internal/core/ideate/record.go:397 — "case p.SchemaVersion != SchemaVersion: return Payload{}, olderSchemaError(p.SchemaVersion)" evidence: internal/core/ideate/record.go:243 — "if err := fsutil.CreateExclusiveIn(root, rel, []byte(body), 0o644); err != nil {"
- cond-2610142138047607 — survived: the command page and the binary change in the same delivered range, a test ties the page's example to the binary's payload type and version, and a v1 payload from an older page gets the refusal naming version 2 and pointing at the command page, at the core and at the CLI evidence: internal/core/ideate/v2_test.go:513 — "if p.SchemaVersion != SchemaVersion {" evidence: internal/core/ideate/record.go:414 — "and a reframing for a reframed verdict (the ideate command page carries the shape)" evidence: internal/surface/cli/ideate_surface_test.go:158 — ""version-1-payload","
- cond-2610142138040936 — survived: every new check is a shape, enum, presence, string-comparison or citation-resolution check; no source is fetched, a ruling is never checked against the overall verdict, and how a reviewer was run is left to the command page evidence: internal/core/ideate/record.go:596 — "The // address is not fetched and the record id is resolved with every other // citation (resolveCitations); this is the shape alone" evidence: internal/core/ideate/record.go:876 — "Whether the ruling was // right is the person's judgement, and the binary never checks it against the // verdict the whole gauntlet reached." evidence: internal/core/ideate/record.go:787 — "conduct is the orchestrating prompt's obligation — the binary cannot observe // how an agent was run"
Grounds
- pursued: an ideate verdict on record today can cite things that do not exist and still pass; we expect that after this change no verdict on record carries an unresolved citation or an unlabelled self-quotation, and the fix also unblocks release cuts, which refuse while its serious issue is open. Shown wrong if a verdict recorded after it still carries one.
Frontmatter
| id | itd-2610141646454347 |
|---|---|
| type | intent |
| lifecycle | shipped |
| kind | standalone |
| severity | major |
.abcd/development/intents/shipped/itd-2610141646454347-an-ideate-verdict-proves-its-own-legs.md
open on GitHub ↗ · commit history ↗
Referenced by10
- adr-2610142116480836Ideate verdicts are private or public by the person's choice, and an idea's source is named publicly only when it graduatesrelated
- adr-2610300908239458Ideate verdicts are private unless the person says publicrelated
- iss-2610141647304982ideate record validates citations only in the record-grill leg: a verdict citing a non-existent intent id in leg 3 and another in leg 1, with 'a remark heard in passing' as a verified primary source, was recorded with exit 0; in the real run a surviving kill attempt rested on a draft presented as shipped (lab finding F-7).related
- iss-2610141647307807A grill whose search silently failed writes the same ideate record as an honest empty grill: a search loop that zsh did not word-split returned zero hits for every term (90 files mention one of them), and the verdict schema carries hits only, with nothing saying what was searched or where (lab finding F-4).related
- iss-2610141647308358ideate's leg 3 asks for one fresh evaluator, which conflicts with the standing Opus+Sonnet review-pair rule under the machine-wide sub-agent cap; fitting the pair into the cap meant batching several ideas into one evaluator context, and one evaluator made a single tool call and never opened the repository it judged (lab finding F-6).related
- iss-2610141647313018ideate's claim status cannot say whether a claim was verified as quoted or as true: a write-up's own experience can only be checked as quotation, and the three statuses carry no such distinction; any non-empty string is accepted as a primary source (lab finding F-9).related
- iss-2610141647315880Nothing defines how two ideate evaluators are merged or where a reframing is recorded: the schema has one adversarial leg, one verdict and no reframing field, although the page says a graduation carries the reframing; runs encode it as a final 'survived' kill attempt by unwritten convention (lab finding F-8).related
- itd-2610141646452213Ideate starts from a source through the librarianbuilt on by
- itd-2610141646456128A private ideate verdict stays privatebuilt on by
- spc-2610142138047122an-ideate-verdict-proves-its-own-legsimplemented by
mentions5
- dcn-26101400000000052026-10-14 — Bundle ideate-librarian (itd-2610141646126492, itd-2610141646452213, itd-2610141646454347, itd-2610141646456128, itd-2610141646467500) is kept out of the v0.16.0 release and built meanwhile, to ship in its own release cut right after v0.16.0 lands (the product thinker's ruling of the day, reversing that afternoon's 'join Friday's release'). Its major issue iss-2610141647304982 therefore must not be open on the default branch when v0.16.0 is cut: the records land after that tag, or with the fix.
- dcn-26101400000000062026-10-14 — Planning interview of bundle ideate-librarian, after two adversarial reviews (design, record discipline; each an Opus and Sonnet pair) returned FIX_FIRST: the verdict fix (itd-2610141646454347) and private ingest (itd-2610141646467500) leave the bundle and ship as soon as each is ready; a verdict whose idea came from a private reading pack is always private; the shared research note is a person-run command, research (itd-2610142116477426), on the librarian's engine, whose agent step is library pack; leg-3 reviewers receive a pack's quoted items only; ideate record takes --private or --public and refuses to guess (adr-2610142116480836). The product thinker's rulings of the day.
- iss-2608230748418054Ideate leg-2 grill has a structural blind spot for id-less records: the 2026-08-23 abcd-research-verb run's grill failed to surface the same-day research note (2026-08-22-sota-research-protocol.md) that answers the submitted idea by name, because research notes carry no citable record id and the grill's output is id-cited hits. The nearest-citable-record note-field rule exists but nothing prompts a sweep of research/notes/ or DECISIONS.md, so a standing verdict recorded outside the id-bearing record families is invisible to the leg that exists to prevent re-litigation. The leg-3 evaluator caught it only by doing its own sweep.
- itd-1Every Intent Has Acceptance Criteria; Every Audit Verifies Them
- itd-104**abcd gates a new idea before it becomes a record entry: research it against primary sources, grill it against the existing record, and let an independent adversary try to kill it.** A product thinker's exciting idea is cheapest to kill in its first hour — and most expensive to kill after it has minted intents, specs, and branches. `/abcd:ideate` runs the admission interview: SOTA research in which every load-bearing claim is checked against its primary source; a record grill — does the brief, an intent, an ADR, or a principle already cover, contradict, or supersede this?; and an independent adversarial review, evaluator outside the loop. The output is a verdict plus a recorded decision with rejected alternatives; only a surviving idea graduates to a draft intent. The protocol is already proven by hand: three ideas entered it on 2026-07-14 — two were killed (one by a measured null result its premise could not survive, one by three independently fatal methodological defects) and one was reframed by adversarial review before a line was built. The manual run also named the two load-bearing steps automation must keep: check the record first (one idea was already written and superseded), and open the primary (three secondary-source claims were falsified, one of them fabricated by tool extraction). "The ideas I'm proudest of are the ones abcd killed in an afternoon," said Iris, a product thinker. "Each one left a recorded reason behind, so I never talk myself back into them six months later."