feat: configurable design-spec save location with arc-owned frontmatter - #54
Merged
Conversation
Add PlansConfig to Config with default "docs/plans", SanitizeSlug and ExpandPlansDir helpers, and the plans package with ReadFrontmatter/EnsureFrontmatter/SetStatus for atomic YAML frontmatter management in design-spec markdown files.
- Replace naive `\n---` byte search with `findClosingDelim`, which requires the
closing delimiter to be exactly `---` followed by `\n`, `\r`, or EOF — preventing
`----` and `--- x` lines from being mistaken for the closer.
- Return `[]byte{}` (not nil) when the closing `---` has no trailing newline,
so no body content is silently dropped on EOF-terminated files.
- Fix `SetStatus` CRLF handling: trim `\r\n` (not just `\n`) when scanning for
the closing delimiter, and preserve the original line ending (`\r\n` or `\n`)
when rewriting the `status:` line.
- Harden existing `os.WriteFile` setup calls in tests to check error returns.
Adds plans.dir validation (empty, .., unknown template vars) to Validate(),
wires plans.dir into config get/set, and adds --resolved flag to config get
for expanding {project}/{prefix} vars to an absolute path.
- Replace inline regexp.MustCompile in Validate() with the package-level templateVarRe from template.go; remove now-unused "regexp" import. - Break on first unknown template variable so the first offender is reported rather than the last (last-write-wins bug). - Check os.Getwd() error in --resolved handler and return a wrapped error. - Return an explicit error when --resolved is used with a key other than plans.dir instead of silently ignoring the flag.
On `arc plan create`, injects YAML frontmatter (title/date/project/status/tags/arc_review) into the plan file via plans.EnsureFrontmatter unless --no-frontmatter is passed. Derives title from first H1 heading or filename fallback. On `arc plan approve`, best-effort syncs the frontmatter status field to "approved" via plans.SetStatus.
…eTitle tests - Add scanner.Err() check in deriveTitle with a stderr warning on mid-read I/O error - Convert all fmt.Fprintf(os.Stderr) calls to the _, _ = idiom used across the codebase - Replace inline var block for titleFlag/noFrontmatter with per-var doc-comment style matching paths.go - Clarify deriveTitle doc comment: matches exactly `# ` (## lines intentionally excluded) - Add three new edge-case tests: non-existent path, empty file, H2-only heading
Match local linter version. Add tagliatelle yaml:snake (Obsidian-queryable frontmatter tags) and relax goconst on _test.go files (mirrors the existing dupl test exclusion).
Behavior-preserving cleanup: extract repeated string literals into named constants (config keys, command verbs, statuses via types.Status*), discard unhandled best-effort errors, replace magic numbers, switch over if/else chains, move whitebox tests to _test packages, errors.Is for sentinels, 0o600 test file perms, gofmt/gofumpt formatting, and doc-comment density.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements epic arc-0d80.06kiqm — a per-project, configurable save location for design specs, with arc-owned YAML frontmatter whose
status:stays truthful acrossarc plan approve.Zero migration: with
plans.dirunset, behavior is exactly as today (docs/plans/). No new command surface — folded into existingarc config/arc plan.What changed
Foundation (
internal/config,internal/plans)PlansConfig{Dir}onConfig, defaulting todocs/plansExpandPlansDir/SanitizeSlug—{project}/{prefix}template expansion,~and relative-to-cwd resolutioninternal/planspackage:ReadFrontmatter/EnsureFrontmatter/SetStatus(+ErrNoFrontmatter), atomic writes; robust against horizontal-rule---lines in the body, EOF without trailing newline, and CRLF filesConfig surface (
cmd/arc/config.go,internal/config/validate.go)plans.dirvalidation: rejects empty,.., and unknown template vars (onlyproject/prefixallowed)arc config get/set plans.dirarc config get plans.dir --resolved→ absolute per-project directory; hard-errors when no project resolves, errors on unsupported keysFrontmatter lifecycle (
cmd/arc/plan.go)arc plan createinjects frontmatter (title/date/project/status: in_review/tags: [arc, design-spec]/arc_review: {kind: legacy, id});--titleoverride and--no-frontmatteropt-out;deriveTitlepulls the first#heading, else the filename sansYYYY-MM-DD-prefixarc plan approvesyncsstatus: approved(surgical line replace, best-effort — never fails the approve)Design constraint
Legacy planner only — no
arc shareintegration (per reviewer guidance, encrypted sharing is being removed).arc_review.kindis alwayslegacy.Testing
go test -race ./...green (16 packages); unit tests for templating, frontmatter parse/write/CRLF/EOF, validation, andderiveTitleedge casesplan createinjects frontmatter with H1-derived title and preserves a body---rule;plan approveflips status toapproved;--no-frontmatterskips injectionOut of scope
T5 brainstorm-skill wiring ships as a separate
agent-marketplacePR.Design:
docs/plans/2026-06-07-configurable-spec-save-location.md(kept untracked).Closes arc-0d80.06kiqm.