ideate

package
v0.13.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 7, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package ideate is the deterministic frame around abcd's idea-admission protocol (itd-104, spc-18): the three-leg gauntlet an idea may be put through before it is allowed to become a record entry.

DIVISION OF LABOUR. Ideate follows the pattern the disembark synthesis family proved. The HOST runs the judgement — primary-source research, a grill against the existing record, and an independent adversarial review — as agents. This package is the frame: it validates what those legs produced, proves every citation names a real record, and writes the durable verdict. It never fetches a source, never calls a model, and never decides whether an idea is any good.

WHY A RECORD EITHER WAY. The verdict is written whether the idea survives or dies, and it must carry the alternatives that were rejected. A killed idea with no recorded reason is an idea that gets re-proposed in six months; the research directory is where a session looks before re-proposing one. That is why an empty rejected-alternatives list is admissible only behind an explicit marker — silence by omission is exactly the failure the record exists to prevent.

NEVER A GATE. Nothing here is reachable from capture or intent-create. Ideate is an optional verb the routing help names for big, unproven ideas; skipping it is never warned about, and no other verb requires it.

Index

Constants

View Source
const (
	ResearchRelDir  = ".abcd/development/research/notes"
	DecisionsRelDir = ".abcd/work/DECISIONS.md"
)

ResearchRelDir is the one directory a verdict record is ever written into, and DecisionsRelDir is the log its dated pointer is appended to. Both are constants rather than parameters: a configurable target would let a verdict land somewhere no future session looks.

It is the research store's notes/ bucket rather than its root because a verdict record IS a dated research note (spc-18: "killed ideas are research outcomes, and the research directory is where a future session looks before re-proposing one"), and research/README.md files dated notes under notes/. Writing to the root would make the shipped binary the standing producer of the very convention violation iss-42 cleared out of that directory by hand.

View Source
const MaxPayloadBytes = 1 << 20 // 1 MiB

MaxPayloadBytes caps the untrusted verdict document, mirroring the synthesis and release-ingest ceilings. It is exported so a front door can bound its READ at the same ceiling the core enforces — the front-door bound is a convenience, this one is the guarantee.

View Source
const MaxSlugLen = 64

MaxSlugLen bounds the idea slug, which reaches a filename. Paired with slugRe it is the lexical half of the write-containment defence.

View Source
const SchemaVersion = 1

SchemaVersion stamps the verdict payload. It is versioned independently of every other abcd artifact, so a future breaking change to the shape is detectable rather than silently misread by a host composing against the old one.

Variables

This section is empty.

Functions

This section is empty.

Types

type CitationError

type CitationError struct {
	Unresolved []string
}

CitationError is the record-grill citation gate's refusal: a hit names a record id that does not resolve in this repository, so the grill is reporting on something that is not there. It names every offending id, because a composer handed one id at a time re-runs the whole gauntlet once per mistake.

func (*CitationError) Error

func (e *CitationError) Error() string

type Claim

type Claim struct {
	Claim         string      `json:"claim"`
	PrimarySource string      `json:"primary_source"`
	Status        ClaimStatus `json:"status"`
}

Claim is one load-bearing claim and the primary source it was checked against.

The binary cannot judge whether a source is PRIMARY — that is the researching agent's obligation, stated in its prompt. What the binary enforces is that a source is named at all, for every claim including an unverifiable one: a claims table row with no source is indistinguishable from an unchecked assertion.

type ClaimStatus

type ClaimStatus string

ClaimStatus is what checking a claim against its primary source established.

const (
	// ClaimVerified: the primary source supports the claim.
	ClaimVerified ClaimStatus = "verified"
	// ClaimFalsified: the primary source contradicts it. Three of these killed an
	// idea in the proven manual run.
	ClaimFalsified ClaimStatus = "falsified"
	// ClaimUnverifiable: no primary source settles it either way. This is an
	// honest outcome, not a pass.
	ClaimUnverifiable ClaimStatus = "unverifiable"
)

type ClaimTally

type ClaimTally struct {
	Verified     int `json:"verified"`
	Falsified    int `json:"falsified"`
	Unverifiable int `json:"unverifiable"`
}

ClaimTally counts the research leg's outcomes, so a reader of the result knows what the claims table found without re-reading the record.

func (ClaimTally) Total

func (t ClaimTally) Total() int

Total reports how many claims the research leg carried.

type ExistsError

type ExistsError struct {
	Path string
}

ExistsError is the no-overwrite refusal: a verdict record for this slug already exists under today's date. A second run is either a re-run of the same gauntlet (in which case the first record stands) or a different one (in which case it deserves its own slug); silently replacing the first would erase a recorded reason, which is the one thing this verb exists to preserve.

func (*ExistsError) Error

func (e *ExistsError) Error() string

type GrillHit

type GrillHit struct {
	Record   string   `json:"record"`
	Relation Relation `json:"relation"`
	Note     string   `json:"note,omitempty"`
}

GrillHit is one existing record entry that bears on the idea. Record is a cited id and MUST resolve in the repository; Note is optional prose, and is where a reference to a brief section or a principle lives, since neither carries a per-entry id that could be cited.

type KillAttempt

type KillAttempt struct {
	Attempt string      `json:"attempt"`
	Outcome KillOutcome `json:"outcome"`
}

KillAttempt is one adversarial attempt on the idea and what it achieved.

type KillOutcome

type KillOutcome string

KillOutcome is what one adversarial kill attempt achieved.

const (
	// KillSurvived: the idea withstood the attempt.
	KillSurvived KillOutcome = "survived"
	// KillPartial: the attempt landed but is not on its own fatal.
	KillPartial KillOutcome = "partial"
	// KillFatal: the attempt is fatal to the idea as posed.
	KillFatal KillOutcome = "fatal"
)

type Leg

type Leg struct {
	Kind         LegKind       `json:"kind"`
	Claims       []Claim       `json:"claims,omitempty"`
	Hits         []GrillHit    `json:"hits,omitempty"`
	KillAttempts []KillAttempt `json:"kill_attempts,omitempty"`
}

Leg is one leg of the gauntlet. It is a union over the three kinds: each kind populates exactly its own evidence field, and a leg carrying another kind's field is a refusal — a payload that smuggles grill hits into the research leg is not a leg abcd knows how to read.

type LegKind

type LegKind string

LegKind identifies one leg of the gauntlet. The three values are also the REQUIRED ORDER: research grounds the idea, the grill checks it against what is already written, and only then does an evaluator who did none of that work try to kill it. Running them in any other order changes what each leg is looking at, so the order is validated rather than assumed.

const (
	// LegResearch: every load-bearing claim checked against its primary source.
	LegResearch LegKind = "research"
	// LegRecordGrill: does the existing record cover, contradict, or supersede
	// this? Every hit is cited by record id.
	LegRecordGrill LegKind = "record-grill"
	// LegAdversarialReview: a fresh-context, off-policy evaluator that did not
	// conduct the research, receiving the idea as an artefact of unknown
	// authorship, trying to kill it.
	LegAdversarialReview LegKind = "adversarial-review"
)

type MissingDecisionsError

type MissingDecisionsError struct {
	Path string
}

MissingDecisionsError is the refusal for a repository with no decision log. The pointer is not decoration: the log is where a session looks for what was already decided, and a verdict record nothing points at is a record nobody finds.

func (*MissingDecisionsError) Error

func (e *MissingDecisionsError) Error() string

type Payload

type Payload struct {
	// SchemaVersion must equal SchemaVersion.
	SchemaVersion int `json:"schema_version"`
	// PromptVersion is the orchestrating prompt's semver (itd-5).
	PromptVersion string `json:"prompt_version"`
	// Idea is the idea as captured, in the words it entered the gauntlet with.
	Idea string `json:"idea"`
	// Legs are the three legs, IN ORDER. An array rather than three named fields
	// precisely so the order is a property of the document the binary can check.
	Legs []Leg `json:"legs"`
	// Verdict is the admission outcome.
	Verdict Verdict `json:"verdict"`
	// RejectedAlternatives are the options turned down on the way to the verdict.
	RejectedAlternatives []RejectedAlternative `json:"rejected_alternatives"`
	// NoRejectedAlternatives is the EXPLICIT empty marker. An empty list is
	// admissible only with this set; omission is not, and setting it beside a
	// populated list is a contradiction the document is refused for.
	NoRejectedAlternatives bool `json:"no_rejected_alternatives,omitempty"`
}

Payload is the whole untrusted host-composed verdict document.

type RedactionResidualError added in v0.8.0

type RedactionResidualError struct {
	Residual []scanner.Finding
}

RedactionResidualError is the stage-two refusal: the rendered record or its pointer still carries a span the redactor must not commit. It names the KINDS and nothing else — a refusal that quoted the span it refused would publish the leak into the error message, and from there into a terminal and a log.

func (*RedactionResidualError) Error added in v0.8.0

func (e *RedactionResidualError) Error() string

type RejectedAlternative

type RejectedAlternative struct {
	Alternative string `json:"alternative"`
	WhyRejected string `json:"why_rejected"`
}

RejectedAlternative is one option considered and turned down, with the reason. Both fields are required: an alternative with no reason records that a choice existed without recording why it was not taken, which is the half of the entry that stops the re-litigation.

type Relation

type Relation string

Relation is how an existing record entry bears on the idea.

const (
	// RelationCovered: the entry already says this.
	RelationCovered Relation = "covered"
	// RelationContradicted: the entry rules this out.
	RelationContradicted Relation = "contradicted"
	// RelationSuperseded: the entry has moved past this.
	RelationSuperseded Relation = "superseded"
)

type Result

type Result struct {
	Slug    string  `json:"slug"`
	Idea    string  `json:"idea"`
	Verdict Verdict `json:"verdict"`
	// Graduates reports whether this verdict admits the idea to a draft intent.
	// Ideate mints nothing itself; this is the signal the surface relays.
	Graduates bool `json:"graduates"`
	// Path is the verdict record, repo-relative.
	Path string `json:"path"`
	// DecisionPath is the log the dated pointer was appended to, repo-relative.
	DecisionPath string `json:"decision_path"`
	// Claims tallies the research leg.
	Claims ClaimTally `json:"claims"`
	// GrillHits counts the record-grill hits.
	GrillHits int `json:"grill_hits"`
	// CitedRecords is the sorted set of record ids the grill cited, every one of
	// them proved to resolve — the citation gate's proof, so a reviewer can check
	// the record without re-running the resolution.
	CitedRecords []string `json:"cited_records"`
	// KillAttempts counts the adversarial leg's attempts.
	KillAttempts int `json:"kill_attempts"`
	// RejectedAlternatives counts the recorded alternatives.
	RejectedAlternatives int `json:"rejected_alternatives"`
	// Redactions counts the secret/PII spans the store-before-commit redactor
	// rewrote out of the verdict's free text on the way to the record. It is
	// reported rather than kept quiet because redacting in silence is how a
	// composer learns nothing about the credential it just pasted (loud-staging).
	Redactions int `json:"redactions"`
}

Result is the transport-agnostic outcome of `ideate record`. It is returned only when the record was written; every refusal is an error, so a caller holding a Result knows a durable record exists.

func Record

func Record(repoRoot, slug string, raw []byte, at time.Time) (Result, error)

Record validates the host-composed verdict for slug and writes it.

at is the clock, passed in rather than read: the date lands in a durable filename and in a durable log line, so it is an input a test can pin instead of a wall-clock read buried in a writer.

Every refusal returns BEFORE the first write, with one exception that is handled rather than accepted: if appending the pointer fails after the record landed, the record is removed again, so a run never leaves half a decision behind.

Both artefacts it writes are COMMITTED, so both go through the store-before-commit redactor (redact.go): the verdict's free text is redacted field by field on the way into the validated document, the rendered record and its decision-log pointer are re-scanned before either is written, and a scanner that cannot be trusted refuses the run rather than recording under it.

type SlugError

type SlugError struct {
	Slug   string
	Reason string
}

SlugError is the refusal for an idea slug that cannot safely become a filename. It is typed because it is an OPERAND fault, not a payload fault: the fix is to re-run with a different slug, not to re-compose the verdict.

func (*SlugError) Error

func (e *SlugError) Error() string

type Verdict

type Verdict string

Verdict is the admission outcome. The set is CLOSED: an unregistered verdict is a whole-document refusal, never a coercion, because the verdict is the one line of the record a later session reads first.

const (
	// VerdictSurvives: the idea cleared all three legs and may graduate to a draft
	// intent through the existing quoted-text create. Ideate mints no intent itself.
	VerdictSurvives Verdict = "survives"
	// VerdictKilled: a leg was fatal. The record is the reason it stays dead.
	VerdictKilled Verdict = "killed"
	// VerdictReframed: the idea as posed does not survive, but a reframing of it
	// does; the record carries both.
	VerdictReframed Verdict = "reframed"
)

func (Verdict) Valid

func (v Verdict) Valid() bool

Valid reports whether v is a registered verdict.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL