Skip to content

fix: best-effort fallback when slash line is invalid grammar#217

Merged
nettapper merged 3 commits into
kitproj:mainfrom
nettapper:task-parser-fails-on-invalid-slash-command
May 26, 2026
Merged

fix: best-effort fallback when slash line is invalid grammar#217
nettapper merged 3 commits into
kitproj:mainfrom
nettapper:task-parser-fails-on-invalid-slash-command

Conversation

@nettapper
Copy link
Copy Markdown
Collaborator

Summary

ParseTask aborted the entire task with a hard error when a line outside a
fenced code block started with / but wasn't a valid slash command (e.g.
//, lone /, /=foo). One bad line in an appended prompt was enough
to discard the entire prompt, the frontmatter, and every sibling task in the
directory.

In-fence slashes were already handled by the markdown-aware splitter; only
the bare-text case was broken.

Also threads a *slog.Logger through the parse entry points so callers can
route the new WARN logs through their own logger instead of slog.Default().

What changed

  • pkg/codingcontext/taskparser/markdownparser.goparseGrammar now
    falls back to a line-by-line parse on failure. Lines that still don't parse
    are emitted as raw text and a WARN is logged with the offending line and
    the underlying grammar error. The whole-segment fast path is unchanged for
    valid input.

  • API additions (backward compatible):

    • taskparser.NewExtension(logger *slog.Logger) goldmark.Extender — the
      existing Extension global is now NewExtension(nil).
    • taskparser.ParseTaskWithLogger(text, logger)ParseTask is a
      nil-logger wrapper.
    • markdown.ParseMarkdownFileWithLogger(path, fm, logger)
      ParseMarkdownFile is a nil-logger wrapper.

    Nil loggers resolve to slog.Default() at log time (not capture time), so
    slog.SetDefault changes made after package load still take effect.

  • pkg/codingcontext/context.go — all five ParseMarkdownFile call
    sites and the one ParseTask call site now pass cc.logger, so parser
    WARNs route through whatever logger was injected via
    codingcontext.WithLogger.

  • pkg/codingcontext/taskparser/taskparser_test.go — new coverage:

    • TestParseTask_BestEffortMalformedSlash — three subcases (//, lone
      /, /=foo) outside a code block, each asserting both the malformed
      line and the surrounding text survive (guards against a fix that
      salvages only the bad line).
    • TestParseTaskWithLogger_RoutesWarnToInjectedLogger — captures stderr
      from an injected logger and verifies the WARN lands there.
    • TestParseTaskWithLogger_NilLoggerResolvesLazyDefault — uses
      slog.SetDefault after construction to confirm nil-logger callers get
      the current default, not a snapshot.

Out of scope

  • The grammar itself is unchanged. A larger rewrite (e.g. teaching the
    grammar to treat any unrecognized /-prefixed line as text directly) would
    be cleaner but riskier.

nettapper added 3 commits May 26, 2026 08:41
ParseTask currently aborts the entire parse when a line starts with `/`
but isn't a valid slash command (e.g. `//`, lone `/`, `/=foo`). These
should fall through to text, preserving both the malformed line and any
surrounding content.

In-fence slashes are already handled by the markdown-aware path; only
the bare-text case is broken.
A single malformed slash line (e.g. `//`, lone `/`, `/=foo`) outside a
fenced code block caused ParseTask to abort the entire task, silently
dropping the user prompt and frontmatter and corrupting downstream
runs.

On parser failure, parseGrammar now re-attempts line-by-line. Lines
that still fail are emitted as raw text with a WARN logged at
slog.Default. The whole-segment fast path is unchanged for valid input.
Adds *WithLogger variants for the parse entry points so callers can route
taskparser's best-effort fallback WARN logs through their own slog.Logger
instead of slog.Default():

- taskparser.NewExtension(logger)        (Extension stays as NewExtension(nil))
- taskparser.ParseTaskWithLogger(text, logger)
- markdown.ParseMarkdownFileWithLogger(path, fm, logger)

The original ParseTask/ParseMarkdownFile/Extension entry points are
preserved as nil-logger wrappers, so external callers are unaffected.

A nil logger resolves to slog.Default() at log time (via loggerOrDefault)
rather than being captured at construction, so slog.SetDefault changes
made after package load still take effect. A new test covers this.

All five ParseMarkdownFile callsites and the ParseTask callsite in
pkg/codingcontext/context.go now pass cc.logger.
@nettapper nettapper merged commit 8eaf28b into kitproj:main May 26, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants