Development record
abcd-intent-quoted-text-create-symmetric
Summary
spc-7 delivers itd-46: abcd intent "<text>" becomes the sub-verb-free create
path, symmetric with abcd capture "<text>". The three invocation shapes are
now crisp on both ledger commands — bare → read-only status, quoted text →
create, sub-verb + id → act on existing. A seeded draft lands in
intents/drafts/, abcd intent new survives as a deprecation-warning alias
for one transition period, the skill-surface promote flow hands an issue's
text to the create verb (engine-backed at last), and both bare renders carry
the one-line capture-vs-intent decision rule.
Approach
Mirrors the shipped capture engine where it applies and the spec store where it is stronger:
- Engine:
internal/core/intent/create.go—CreateFromTextderives a kebab slug (non-alphanumeric runs →-, trimmed, capped 60 chars, validated againstslugRebefore it becomes a filename), refuses empty/whitespace text before any write, mintsitd-Nunder an exclusive flock on theintents/directory fd (O_NOFOLLOW, 5s timeout, no lock artifact in the record tree — the spec store's mint-lock pattern, not capture's), scanning max N across all five buckets inside the lock, and writes atomically viafsutil.WriteFileAtomic. - CLI wiring:
newIntentCommandaccepts a positional; bare invocation keeps rendering read-only status (the create branch is guarded onlen(args) > 0);newis a registered alias that routes to the same create call and prints the deprecation warning on stderr only, keeping stdout byte-identical to the quoted-text form. - Canonical seeded-draft shape (the record this spec defines):
frontmatter
id,slug,spec_id: null,kind: null,suggested_kind: null,reclassification_history: [],builds_on: [],severity: minor; body# <title from whitespace-collapsed seed>,## Press Release(placeholder blockquote),## Why This Matters(the raw seed text),## Acceptance Criteria(itd-1 discipline placeholder, no bullets — the human fills them beforeintent plan),## Open Questions,## Audit Notes. The shape passesintent.Validateand theintent_lifecyclerecord-lint drafts rule by construction (verified by a test that runs the real lint engine).
Adjudications (deviations from the intent letter, recorded not hidden)
- "Byte-identical to
intent new" is historical. Nointent newverb ever existed in the Go CLI (it was a pre-rebuild markdown-surface shape); the seeded shape defined above IS the canonical artefact, and the alias produces it identically. - Two scope bullets name files that do not exist in this tree —
commands/abcd/intent.mdanddocs/reference/commands.mdare old-system references. The intent verb family has no plugin markdown surface at all (pre-existing boundary gap, now on the ledger as iss-105); the routing/table updates those bullets describe have no native counterpart to edit. The AC-level requirements they served (create path reachable, decision rule visible) are met at the surfaces that do exist. - Typo-guard asymmetry accepted for now: unlike capture's
suspectedTypoedSubcommandheuristic, any non-sub-verb first token is create text, so a mistyped sub-verb files a draft instead of erroring. The ACs do not require the guard; recorded as iss-104 for a symmetry follow-up.
Milestones as delivered
- Create engine + tests (
a9329f8), watched-fail first. - CLI wiring: quoted-text route,
newalias + stderr warning, bare no-mutation pin, decision rule in both bare renders (c3d4d0a). - Skill-surface re-plumb:
commands/abcd/capture.mdpromote now hands the issue body toabcd intent "<text>"and honestly states therelated_intentsback-link is still verb-less (spc-6 AC3 stays open on it); CHANGELOG entry (7289aa5).
Acceptance-criteria satisfaction
AC as ordered in itd-46 → evidence (tests in
internal/surface/cli/intent_cli_test.go unless noted):
- Quoted text creates a seeded draft —
TestIntentQuotedTextCreates; engine:internal/core/intent/create_test.go(TestCreateFromTextSeedsDraft,TestCreateFromTextAllocatesNextID,TestCreateFromTextPassesRecordLint— the real lint engine over the created file). Watched-fail captured before implementation (undefinedCreateFromText; CLIunknown command). newroutes to create with a clear transition signal — lean (a) as pre-adjudicated:TestIntentNewAliasWarnsAndCreates(stdout identical, deprecation warning on stderr only). Watched-fail:unknown command "new".- Bare invocation stays status-only —
TestIntentBareCreatesNothing(status render asserted, zero files underdrafts/). - Promote hands the issue text to the create path —
commands/abcd/capture.mdpromote section now routes throughabcd intent "<text>", which the AC1 tests cover end-to-end; the markdown states exactly what promote does and does not do today. - The decision rule is visible in both bare helps —
TestBareHelpsCarryDecisionRule(both bare renders contain the nitpick→capture / user-facing-change→intent rule). Watched-fail: bare intent render lacked the rule.
Out-of-scope confirmations: no ID-scheme rename, no command merge, mutating
sub-verbs keep requiring ids, iss-101/iss-102 code untouched.
Frontmatter
| id | spc-7 |
|---|---|
| type | spec |
| lifecycle | closed |
.abcd/development/specs/closed/spc-7-abcd-intent-quoted-text-create-symmetric.md
open on GitHub ↗ · commit history ↗
References1
- itd-46`/abcd:intent "<text>"` And `/abcd:capture "<text>"` Become Symmetric Create Pathsimplements
mentions14
- dcn-26071700000000032026-07-17 — Burst 4 (M4, itd-46/spc-7): quoted-text create shipped; the
- iss-101registerRepo does an unlocked load-modify-write of ~/.abcd/history/index.json (loadHistoryIndex -> mutate -> writeHistoryIndex, apply.go), and bootstrapHistory is check-then-act. WriteFileAtomic prevents torn files but not lost updates (last rename wins), so two concurrent 'abcd ahoy install' runs from different worktrees can drop a repo registration or clobber the registry with an empty Repos list, erasing lineage/supersedes links set on explicit user confirmation. Deferred from the multi-agent bug hunt (was B30): a correct fix needs an inter-process O_EXCL/flock lock in ~/.abcd/history held across the load-mutate-write, with a re-load inside the lock and bootstrapHistory creating index.json with O_EXCL. The registerRepo sequence spans an interactive prompter.Confirm (re-founding lineage), so the lock must NOT be held across that prompt (risk of blocking other processes on user input). Low severity, self-heals on next install. Needs a dedicated change.
- iss-102cleanOrphanPlaceholders (capture/alloc.go) runs locklessly from mutationPreamble at the start of every Capture/Resolve/Wontfix, while commitCapture (workflow.go) fills the reserved placeholder OUTSIDE the ledger flock. A capture stalled >60s between reservation and commit can have its just-committed issue file deleted: the sweep's Lstat passes (size 0, aged) then the stalled commit's atomic rename lands, then the sweep's os.Remove deletes the fully-committed file — after Capture returned success. Deferred from the multi-agent bug hunt (was B26): a pre-unlink re-check (orphanStillRemovable: re-Lstat + SameFile + size==0) already mitigates the common interleaving, but full elimination requires running the sweep AND the commit write under withLedgerLock (the commit write path is in workflow.go). Low severity; data-loss only under a >60s stall plus concurrency.
- iss-104intent quoted-text create treats any non-subcommand first token as create text (no suspectedTypoedSubcommand guard like capture): 'abcd intent paln "x"' files a draft instead of erroring; consider porting capture's typo heuristic for symmetry
- iss-105the intent verb family (plan/link/review/ingest and now quoted-text create) has no plugin markdown surface (no commands/abcd/intent.md) — pre-existing gap against the wired-from-both-surfaces boundary; itd-46's old-system scope bullets assumed one existed
- iss-2609290448510918The predecessor-store qualifier sweep reaches past the six intents iss-2609261536147903 named: outside drafts/ and those six, 192 citation sites across about forty intents name a spc-N at or below spc-70 that is not the citing intent's own spec, with no '(predecessor store)' on the line. Some are live cross-references (the cold-reading family's intents cite each other's live specs), and some are the predecessor store's (itd-4, itd-6, itd-29, itd-47 and itd-49 describe pre-rebuild work in its terms), so each site needs the same reading against the live spec it collides with; the specs charter's Two spc-N Namespaces section is the rule. Found while sweeping the pattern of iss-2609261536147903 in lane drainDrift3; a census script over intents/{planned,shipped,disciplines} reproduces the count.
- itd-1Every Intent Has Acceptance Criteria; Every Audit Verifies Them
- itd-4Nothing You Notice Gets Lost
- itd-28Spec-Tied Reviews Live Next To The Spec They Reviewed
- itd-33Multiple Agents Coordinate On Intent And Spec Work Without Duplicating Effort Or Producing Competing Artefacts
- itd-37Every Spec Externalises How It Should And Shouldn't Be Modified
- itd-2609111003026787A gate catches the intent whose work is live while its spec is still open
- spc-6issue-capture
- spc-2609120450289528A gate catches the intent whose work is live while its spec is still open