This is the record of an abcd-managed repository

abcdBetav0.16.2 · 2026-11-06

Development record

Every question abcd asks passes its check before the person sees it

specopenspc-2610301917245442

Summary

Delivers itd-2610230756259215, which resolves iss-2610230745597968 and iss-2610301855200679. One decision function in internal/core/question judges a question as the guard hook judges it today, and two callers share it: The hook, and a new read-only sub-verb, abcd guard question, that reports every fault grouped by rule and tab, the rows of each tab, what the hook would do with the draft in this checkout now, the binary's version, and the input ready to ask. The hook's refusal loses its ten-part cut and names every rule on every tab it hits, in the words the verb shows, with one line pointing at the verb and the question-writing agent. A question is abcd's only when a header is in abcd's number grammar (Q1, Q1a, Q2/5), whatever the status line says; the older role-word form (Product Q2) is still read as abcd's. The abcd:question-drafter agent becomes abcd's question-writing agent: It writes, runs the verb, fixes every fault, and checks again, at most five checks to a part, splits what does not fit into numbered parts, and returns only a draft the verb exits 0 on, or a refusal to the agent that called it. The GRILL text and the generated pages say so, and the brief's invariant 1 binds to every question carrying abcd's header. Three recorded labs are the evidence for the criteria that rest on a live model.

Grounding, read on 30 October 2026:

  • The hook's whole question path is internal/surface/cli/guard_question.go. questionGate (lines 89 to 112) reads the away record first through awayAtQuestion (lines 119 to 132, autonomy.AwayAt, failing open on autonomy.ErrUnsafe). presentQuestionGate (lines 157 to 238) decodes the host input with decodeQuestions (lines 323 to 340), reads the mode only where the badge shows (ahoy.Managed and mode.HasTier, lines 166 to 173), decides whether the question is abcd's by a chip or by a mode naming somebody (lines 174 to 186), runs question.CheckLimits (lines 195 to 198), refuses unless the findings are rows only (lines 203 to 211), then writes the open marker and the mode (lines 212 to 228) and the rows note (line 233). The decision and the writes are interleaved in one function; nothing else can call the decision.
  • The refusal is writeLimitsRefusal (lines 410 to 439): One head line counting the refusing parts, the modeMadeAbcds line (lines 387 to 394) for a question abcd's only through the mode, then at most maxRefusalParts (10, line 380) finding lines and an "and N more" line, then the rows findings under rowsNotRefusingLine. rowsNote (lines 284 to 295) is bounded the same way and names the drafter.
  • The check is question.CheckLimits (internal/core/question/check.go lines 123 to 142), fourteen rules. The header grammar is chipRe (line 113): A role word from Limits.ChipRoles, a space, Q, a number, and an optional /total; ChipRole (lines 90 to 96) reads the role word, and the register rule falls back to it when no mode names anyone (forProductThinker, lines 387 to 396). The rows rule computes a per-tab split (line 334, splitRows in rows.go lines 25 to 36, unexported) that only the rows finding's text carries.
  • Rule 12 adds one finding per matching line of question text (check.go lines 324 to 329), so the findings grow with the payload, which is why the ten-part cut exists; past the counts the check reads only the first four tabs and four options (lines 138, 211).
  • The structured question type is question.Ask (ask.go lines 14 to 44); Check (lines 98 to 193) is its structural check and Fields (lines 209 to 243) composes the host's field view, leaving out a Now: or Change later: line the question does not carry (lines 216 to 225) so the limits check names the gap.
  • The asking rules are question.AskingRules (asking.go lines 35 to 95): The layout rule names the chip form (lines 51 to 61), the drafter rule (lines 63 to 66), and the chip-sets-the-mode rule (lines 93 to 94). internal/core/rules/grill.go lines 25 to 31 generate the GRILL domain from them, and cmd/asking-sync/main.go (targets, lines 59 to 63; renderDrafter, lines 86 to 101) writes them into commands/intent.md and agents/question-drafter.md (make asking-sync, make asking-sync-check, Makefile lines 280 to 284).
  • The drafter page (agents/question-drafter.md) returns questions, rows and remaining (lines 45 to 76), refuses only a missing or contradictory part (lines 39 to 41, 77 to 78), and counts rows by hand from the generated block (lines 107 to 115).
  • The hook reads its payload under maxHookStdinBytes (1 MiB, internal/surface/cli/cli.go line 1622; guard.go lines 354 to 360) and dispatches the question tool at guard.go lines 382 to 384. The guard's sub-verbs register at guard.go lines 199 to 203. guard check exits 0 on allow and warn, 1 on block, 2 when it cannot answer (brief 17-guard.md lines 51 to 80).
  • The binary's version is core.Version (internal/core/core.go line 18). The mode store is mode.ReadAt and the marker mode.MarkQuestionOpen (internal/core/mode/question.go lines 56 to 73). The lab store is ~/.abcd.noindex/lab/<root-sha>/<lab-id>/ with one probe record per probe (internal/core/lab/lab.go lines 1 to 28).
  • The brief's guard chapter carries a machine-checked sub-verbs table (17-guard.md lines 19 to 34, surface_coverage against .abcd/development/release/surface.json, internal/core/lint/config.go lines 186 to 191) and the question gate section (lines 199 to 323). Invariant 1 is 02-constraints/03-invariants.md line 9. roles_vocabulary_test.go lines 112 to 125 accepts a chip as a page's way of naming a question's addressee.
  • One fixture exists: internal/surface/cli/testdata/questions/routing-2026-10-03.json with its findings file.

Approach

  1. The decision function. question.Decide(f Fields, s State, l Limits) Verdict is lifted out of presentQuestionGate. State is what the hook reads before it decides: Whether the person is away and whether that record was read, the mode's person and whether the store was read, whether the badge shows, and the binary's verbs. Verdict carries the outcome, the findings, the per-tab addressee with an addressee_changes note where an older-form chip would re-set the mode (decision 15), the rows split of every tab, whether the result is partial and which stores and rules it could not apply, and whether the verdict depends on state (decision 5). The outcomes are decision 3's: admit, admit-with-rows-note, refuse, refuse-away, admit-unchecked, and unchecked. Order is the hook's today: Away first (refuse-away before any limit), then the header test, then the limits. admit-unchecked is any call with no header in abcd's grammar, in every mode state, since decision 36 supersedes the parenthesis in decision 3. unchecked is each fail-open path: An away record or mode store that could not be read where the rules needed it, marked partial with unread naming the store and the rules it decides (the register rule for the mode, every rule for the away record). The rows split is exported as question.Rows(t Tab, l Limits) RowSplit with the fields header_and_frame, question_text, options and total, the figures splitRows already computes.
  2. One store reader. questionState(cmd, cwd) (question.State, []unread) in internal/surface/cli holds the reads both callers make: awayAtQuestion, mode.Root, ahoy.Managed, mode.HasTier, mode.ReadAt, and verbsOf. The hook calls it and then Decide, and acts on the verdict as it acts today: A deny for refuse and refuse-away, the marker and the mode for an admission where the badge shows, the rows note for admit-with-rows-note, questionFailOpen for unchecked. The writes stay in the hook, after the decision, so the verb can never reach them.
  3. The verb. abcd guard question [<file>|-] [--ask] [--json], a sibling of guard check, registered beside it. It reads the draft from the file or standard input under maxHookStdinBytes, calls questionState and Decide, and writes nothing (decision 9): No marker, no mode, no branch record, no log. Its help line is in the house form: "Check a draft question as the hook will judge it: Writes nothing; refuses an unreadable draft, a store it cannot read, and any finding." Exit 0 when there is no finding and every store was read; 1 on any finding, a rows overrun alone included; 2 when the input is not JSON, is over the cap, has neither shape, or when a store the rules depend on could not be read, even with no finding (decision 8, invariant 16), and, once spc-2610301950037469 lands, when a question limit the repository or the person set is refused (with refused and no ready) or a layer of the question limits could not be read (the partial result naming the file).
  4. Two input forms, chosen by the flag. Without --ask the input is the host question tool's {"questions": [...]}, decoded as decodeQuestions decodes it (unknown keys kept, a known key of the wrong type unreadable). With --ask it is question.Ask, decoded with unknown fields refused, since abcd owns that schema; question.Check's structural findings are reported under the rule structure, and the limits run over Ask.Fields(), so a missing Now: line is named by the limits check and never invented (decision 14). ready is the input's own bytes for the host form, so multiSelect and every key the check does not read survive, and the composition through Fields rendered as the host's input for --ask; it is absent when the outcome is refuse or refuse-away (decision 7).
  5. The JSON. _type abcd/question-check/v1; version from core.Version; hook (the outcome); depends_on_state; partial and unread; addressee per tab with addressee_changes; findings, each with tab, part, rule, value, limit, remedy and refuses; by_rule, one entry per rule and tab with the first echoed value and a count; rows per tab and rows_limit; ready. spc-2610301950037469 adds three fields to the same type, so its name does not change: limits (rows, label_words, description_sentences, the figures in force), limits_source (the same three keys, each bundled, .abcd/config.json, or ~/.abcd.noindex/config.json where a personal value tightened it), and refused (key, value, bounds, file, and for a personal value that loosens limit and limit_file) on the refusal path; rows_limit equals limits.rows. The text render prints the outcome, the rows per tab, the grouped finding lines from the shared renderer, and the pointer line.
  6. The grouped refusal and one renderer. question.GroupFindings(findings) []Group folds findings into one group per rule and tab, in the order the first finding of each appeared, keeping one echoed value and a count. question.RenderGroups(w, groups) writes one line per group: The tab, the rule, the echoed value, the limit, the remedy, and "and N more such lines" when the count is above one. The deny becomes: The head line counting the refusing findings; the group lines for the refusing rules; rowsNotRefusingLine and the rows groups; and one pointer line, "Check the next draft with abcd guard question before asking it, or draft it through the abcd:question-drafter agent, which does." maxRefusalParts, the "and N more part(s)" line, and modeMadeAbcds go (the mode no longer makes a question abcd's). The bound is the rules times the checked tabs plus the call-level finding and the three fixed lines, because the check already stops at four tabs and four options. The rows note names the verb beside the drafter.
  7. The header grammar, and the mode. The grammar becomes ^(?:(<role>) )?Q[1-9][0-9]*[a-z]?(?:/[1-9][0-9]*)?$: An optional role word from ChipRoles for the older form, then Q, a number, an optional part letter, and an optional total. question.IsAbcds(header, l) replaces hasAbcdChip's test; ChipRole keeps returning the role word, and returns none for the number form. Rule 1's limit and remedy name the number form ("Q1") and say the older form is still read. The hook decides abcd's question by the header alone (decision 36): A call with no header in the grammar is admit-unchecked in every mode, and the branch at guard_question.go lines 175 to 186 goes with it. The register rule follows the mode for the number form: The hook passes the mode's person where the badge shows, and Unnamed elsewhere, where forProductThinker already falls back to an older-form chip's role word. The mode writes: An admitted call with an older-form chip still sets the mode from the chip (chipState); an admitted number-form call sets nothing and is marked open under the current mode, so the answer's reset returns the mode the agent set with abcd mode product-thinker or abcd mode facilitator before asking (decision 37) to managed. Invariant 1 is reworded to bind to every question abcd asks through the host's question tool, each of which carries abcd's header, and to say that the header is what the guard checks the question by.
  8. Pages and the brief. commands/guard.md gains a question section beside check and hook and rewrites the question paragraphs of hook (the header rule, the grouped refusal, the pointer); commands/mode.md says the agent sets the mode before a number-form question and the answer resets it; commands/intent.md and agents/planning-interviewer.md move their examples from the chip to the number form with the mode set first, which TestPluginQuestionBlocksNameTheAddressee already accepts as a setter. The brief's guard chapter gains the question row in its sub-verbs table, the verb's exit rules beside guard check's in "What a person gets back", and a rewritten question gate section; .abcd/development/release/surface.json and docs/reference/cli/commands.md are regenerated with the tree (decision 18).
  9. The writer. agents/question-drafter.md keeps its name (decision 31) and is rewritten as the question-writing agent. It labels every draft with a number header, resolves the binary as every plugin page does, runs abcd guard question after every edit, and returns the verb's ready as questions, its rows, its version, and checked_by: abcd. It loops until the verb exits 0, at most five checks to a part (decision 32). It never refuses for length: Material too long for one screen is split into numbered parts as decision 33 says, independent decisions as tabs of one question (up to four) and a dependent part as the next question; too many answers are the dependent case, Q1a offering the first answers and "None of these", which leads on to Q1b with the rest and "Decide later"; one decision whose material alone is too long is quoted shorter and faithfully, with every answer's gain and cost kept, or split into the decisions it holds. Its refusals have three causes, each returned to the calling agent only: A missing or contradictory part of the brief, the verb's exit 2 (reported as the refusal and never a reason to count by hand, decision 11), and five checks spent, carrying the verb's last findings. Where the installed binary has no guard question, it returns checked_by: hook-only and counts nothing by hand (decision 21); only where the host gives it no shell does it count by hand, checked_by: hand. In abcd's own checkout it runs the verb from the source tree, as the repository's own rule says, and records the version it reports.
  10. GRILL. AskingRules changes as decision 17 (revised) allows: The drafter rule says the writer runs abcd guard question on each draft, returns what the check counted, and loops until it is clean, that the agent running an interview asks only what the writer returns and never writes or edits a question itself, and that a host with no agents checks its own draft with the same verb before asking; the layout rule names the number form (Q1, Q1a, Q2/5) and says the older form is still read; the last rule says the agent sets the mode to the addressee with abcd mode product-thinker or abcd mode facilitator before a question and the answer sets it back. Every rule that opens "In abcd's own interviews" keeps those words. make asking-sync regenerates both pages, and renderDrafter tells the writer to label every draft, run the verb after every edit, and return its rows, ready input and version, keeping the hand count for a host with no shell.
  11. Labs. Three labs on one pinned binary and one fixed material each (decision 22): A managed snapshot repository with the plugin binary and no abcd checkout. Each lab's probes are three recorded runs, each transcript kept in state/probes/<probe>/, and a criterion passes only when all three pass. The pass rules and the fixture files are written into the lab's INTENTION.md before any run, and the harvest is captured into a research note.

How each criterion is met

  • Verb input and exit codes (exit 0, exit 1 on any finding, exit 2 on non-JSON, over-cap or neither shape, --ask). Approaches 3 to 4. Tests: TestGuardQuestionExitsZeroOnACleanDraft, TestGuardQuestionExitsOneOnARowsOverrunAlone, TestGuardQuestionExitsTwoNamingTheShape (a text file, a payload one byte over the cap, and {"questions": 1}), TestGuardQuestionAskComposesTheDraft, in internal/surface/cli/guard_question_verb_test.go.
  • The verb writes nothing. Approach 3. Test: TestGuardQuestionWritesNothing hashes every file of a managed fixture checkout with the mode on the product thinker and no question open, runs the verb on a clean, a refused, and a rows-only draft, and compares the tree byte for byte, the mode file and the marker named first.
  • The verb equals the hook on the corpus; away; unread stores; partial exits 2. Approaches 1 to 3. Tests: TestGuardQuestionAndHookDecideAlike in guard_question_corpus_test.go runs both verbs on every file of testdata/questions/corpus/ (the 21-fault four-tab case, rebuilt with four number-form headers so that it is abcd's under decision 36 and carries 21 findings across the content rules the original broke, the 25-row case, one fault per rule, number-form and older-form headers and no header under each mode state, the person marked away, and an unreadable away record and an unreadable mode store) and asserts the verb's findings and hook equal the hook's deny, note, or silence in the same fixture state; TestGuardQuestionReportsRefuseAwayWhileAway; TestGuardQuestionMarksAPartialResult (outcome unchecked, partial true, unread naming the store and the rules); TestGuardQuestionPartialNeverExitsZero.
  • Not abcd's question; the rows split; 21 findings; one group per rule and tab. Approaches 1, 5, 6. Tests: TestGuardQuestionSaysAnUnlabelledDraftIsNotAbcds (under the product thinker's mode too); TestRowsSplitIsTheChecks in internal/core/question/decide_test.go on the 25-row case (8, 7, 10, 25); TestGuardQuestionListsAllTwentyOneFindings; TestGroupFindingsIsOneRuleOneTab in render_test.go.
  • The deny names every rule on every tab with no "and N more" line; names the verb and the writer; stays bounded on a flood; one line for thousands of em-dash lines; finding lines byte-identical. Approach 6. Tests: TestQuestionDenyNamesEveryRuleAndTab (asserts no "and N more part" line and one line per refusing rule and tab of the 21-fault case); TestQuestionDenyPointsAtTheVerbAndTheWriter; TestQuestionRefusalIsBoundedOnAFlood (rewritten: Thousands of tabs and options, the line count at most the rules times four plus the fixed lines); TestEmDashFloodTakesOneLinePerTab; TestVerbTextAndDenyShareTheirLines (the group lines of the verb's text render and of the deny compared byte for byte).
  • Composition order; a missing Now: is named; ready is the input's bytes. Approach 4. Tests: TestAskComposedGivesMaterialStateThenQuestion (material first, Now: and Change later: next, the ask last, the decide-later option last) and TestAskMissingNowIsAFindingNotALine in internal/core/question/ask_test.go; TestGuardQuestionReadyKeepsUnreadKeys (multiSelect and an unknown key survive untouched).
  • The JSON carries the version. Approach 5. Test: TestGuardQuestionJSONCarriesTheVersion with core.Version set for the test.
  • The writer's page and its fallbacks (the generated block; exit 2 returned as a refusal; no shell counts by hand; the source tree in abcd's checkout; an older binary is hook-only). Approaches 9 to 10. Tests: TestDrafterBlockIsGenerated and TestDrafterBlockTellsTheWriterToRunTheVerb in cmd/asking-sync/main_test.go (the block names the header, the verb after every edit, rows, ready and version, and the hand count only for a host with no shell); TestWriterPageStatesItsRefusalsAndFallbacks in internal/core/lint reads the hand-written part for the three refusal causes, checked_by with its three values, the five-check bound, and the source-tree sentence. The source-tree and older-binary behaviour are judged in the labs below, since they need a model run.
  • The GRILL domain as generated. Approach 10. Tests: TestAskingRulesNameTheVerbAndTheWriter in asking_test.go (the drafter rule and the no-agents sentence name abcd guard question, the one rule saying the interviewing agent asks only what the writer returns, the header rule naming Q1 and Q2/5); TestGrillTextScopesRegisterToAbcdInterviews and TestGrillTextCarriesTheAmendments updated to the new phrases, still asserting the "In abcd's own interviews" prefix; TestGrillDomainIsGeneratedFromTheAskingRules unchanged.
  • The header grammar; an unlabelled question passes unchecked under the product thinker's mode; the register rule follows the mode. Approach 7. Tests: TestHeaderGrammarReadsNumberAndOlderForms in limits_test.go (Q1, Q1a, Q2/5, Q1a/4, Product Q2, Setup Q1/4 read as abcd's; Q0, q1, Q1A, Builder Q1, Pick a theme not); TestUnlabelledQuestionRunsUncheckedWhateverTheMode in guard_question_limits_test.go replaces TestModeMadeQuestionAbcdsSaysSo; TestNumberHeaderRegisterFollowsTheMode (a Q1 tab naming a command is refused under the product thinker's mode and admitted under the facilitator's); TestOlderChipStillSetsTheMode keeps TestGuardChipSetsTheModeWhileManaged's case; TestNumberHeaderLeavesTheModeAndResetsOnTheAnswer.
  • Surfaces and the brief (the command page, the CLI reference, the help line; the sub-verbs row, the exit rules, surface coverage). Approach 8. Tests: TestSentencesAreOneHouseForm and the surface drift test over surface.json; the surface_coverage lint over the brief's table; TestGuardPageDocumentsTheQuestionVerb in internal/core/lint reads commands/guard.md for the question section and docs/reference/cli/commands.md for abcd guard question.
  • Seen by a person: No refusal line before a drafted question; no tab cut at 80 columns; the second ask admitted after a refusal; the check by hand; the status line unchanged by a check; numbered parts; "None of these" leading to Q1b; a header that is only its number. Approaches 3, 6, 7, 9, 11. The by-hand check and the status line are TestGuardQuestionWritesNothing and the verb's text render test TestGuardQuestionTextGroupsRowsAndOutcome. The rest are the labs: In every recorded run the hook's log shows no deny before a writer-returned question, every tab's rows are within 24 by the verb's own count, and the headers are number-form.
  • The writer loop and the re-brief (five checks to a part; the caller asks nothing until a draft passes). Approaches 9 to 10. The bound and the refusal shape are page tests above; the loop itself is judged in the labs, where each run's transcript is read for the verb calls per part and for any question asked after a writer refusal.
  • The labs (the plugin binary works without abcd's checkout; the 25-row material in three runs with no file written and at most three verb calls; the 21-fault question refused and the second ask admitted in three sessions; the material refused for length on 2026-10-30 returned as parts the verb exits 0 on in three runs, none a refusal; three interviews of at least ten questions with no refusal). Approach 11. Lab A (question-writer-25-rows): The writer on the 25-row case, the transcript checked for file writes and verb calls; the case is over the limit only at 24 rows, so the lab's snapshot repository sets question.rows to 24 in its .abcd/config.json, since itd-2610301914350541 raised the default to 32 (a pinned binary predating that intent holds 24 by default and reads no such key). Lab B (question-writer-refusal): A live session told to ask the 21-fault question without the writer, refused, and its second ask admitted. Lab C (question-writer-parts): The writer on the 2026-10-30 material, copied into the lab's INTENTION.md from the session's transcript, every returned part run through the verb, and three interviews of at least ten questions each with the hook's log read for any deny. The first probe of each lab runs abcd guard question --json on a clean draft from the lab's plugin binary, outside any abcd checkout, and records its exit and version.

Out of scope, recorded elsewhere: Per-repository layout limits (itd-2610301914350541); tightening question.Check (decision 14); the content rules the check cannot see (iss-2610090816296964, iss-2610161739587645); the required "Unclear" option (iss-2610150551207288); refusing on the rows limit alone (iss-2610070637562567's ruling stands); and the binary's own Terminal-drawn interviews (the setup interview's Setup Q1, the written interview's Product Q1), which have no status line, so the drawn chip is what names the addressee there and the older form stays theirs.

Footprint

  • packages: internal/core/question/check.go, internal/core/question/decide.go, internal/core/question/render.go, internal/core/question/rows.go, internal/core/question/asking.go, internal/core/question/limits.go, internal/core/question/ask.go, internal/surface/cli/guard_question.go, internal/surface/cli/guard_question_verb.go, internal/surface/cli/guard.go, cmd/asking-sync/main.go, agents/question-drafter.md, agents/planning-interviewer.md, commands/guard.md, commands/mode.md, commands/intent.md, docs/reference/cli/commands.md, .abcd/development/release/surface.json, .abcd/development/brief/04-surfaces/17-guard.md, .abcd/development/brief/02-constraints/03-invariants.md, .abcd/development/research/notes/2026-11-question-writer-labs.md
  • tests: internal/core/question/decide_test.go, internal/core/question/render_test.go, internal/core/question/limits_test.go, internal/core/question/ask_test.go, internal/core/question/asking_test.go, internal/surface/cli/guard_question_verb_test.go, internal/surface/cli/guard_question_corpus_test.go, internal/surface/cli/guard_question_limits_test.go, internal/surface/cli/guard_question_rows_test.go, internal/surface/cli/guard_question_test.go, internal/surface/cli/roles_vocabulary_test.go, internal/surface/cli/testdata/questions/corpus/, internal/core/rules/grill_test.go, internal/core/lint/surfaceagents_test.go, cmd/asking-sync/main_test.go

Steps

  1. The shared decision function and the verb
    • needs: none
    • packages: internal/core/question/decide.go, internal/core/question/rows.go, internal/core/question/ask.go, internal/surface/cli/guard_question.go, internal/surface/cli/guard_question_verb.go, internal/surface/cli/guard.go, commands/guard.md, docs/reference/cli/commands.md, .abcd/development/release/surface.json, .abcd/development/brief/04-surfaces/17-guard.md
    • tests: internal/core/question/decide_test.go, internal/core/question/ask_test.go, internal/surface/cli/guard_question_verb_test.go, internal/surface/cli/guard_question_corpus_test.go, internal/surface/cli/testdata/questions/corpus/, internal/core/lint/surfaceagents_test.go
    • landed: ce643bd05b6f
  2. The grouped refusal and the one renderer
    • needs: 1
    • packages: internal/core/question/render.go, internal/surface/cli/guard_question.go, commands/guard.md, .abcd/development/brief/04-surfaces/17-guard.md
    • tests: internal/core/question/render_test.go, internal/surface/cli/guard_question_limits_test.go, internal/surface/cli/guard_question_rows_test.go
  3. The header grammar, the mode, and invariant 1
    • needs: 1
    • packages: internal/core/question/check.go, internal/core/question/limits.go, internal/core/question/decide.go, internal/surface/cli/guard_question.go, commands/guard.md, commands/mode.md, .abcd/development/brief/04-surfaces/17-guard.md, .abcd/development/brief/02-constraints/03-invariants.md
    • tests: internal/core/question/limits_test.go, internal/surface/cli/guard_question_limits_test.go, internal/surface/cli/guard_question_test.go, internal/surface/cli/guard_question_corpus_test.go
  4. The writer contract, the GRILL text, and the regenerated pages
    • needs: 2, 3
    • packages: internal/core/question/asking.go, cmd/asking-sync/main.go, agents/question-drafter.md, agents/planning-interviewer.md, commands/intent.md, commands/mode.md
    • tests: internal/core/question/asking_test.go, internal/core/rules/grill_test.go, cmd/asking-sync/main_test.go, internal/surface/cli/roles_vocabulary_test.go, internal/core/lint/surfaceagents_test.go
  5. The recorded labs
    • needs: 4
    • packages: .abcd/development/research/notes/2026-11-question-writer-labs.md
    • tests: internal/surface/cli/guard_question_verb_test.go

Frontmatter

idspc-2610301917245442
typespec
lifecycleopen

References1

mentions9