feedback

package
v1.10.0 Latest Latest
Warning

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

Go to latest
Published: Sep 6, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package feedback implements the EKA feedback module (ADR-026): plain files under EKA_HOME/feedback/<id>.md carrying YAML frontmatter + markdown body, and the publish path that files a draft as a GitHub issue on the fixed target repository.

The package is standalone by design: it imports neither store, workspace nor sync (the Runtime Kernel internals), and the CLI passes the home directory in explicitly. Feedback is meta-information about the tool addressed at the EKA maintainers — never a CKO, never a unit of the canonical store.

Index

Constants

View Source
const (
	TypeBug         = "bug"
	TypeSuggestion  = "suggestion"
	TypeImprovement = "improvement"
	TypeQuestion    = "question"
)

Feedback type values (ADR-026 §Decision 1).

View Source
const (
	SeverityLow    = "low"
	SeverityMedium = "medium"
	SeverityHigh   = "high"
)

Feedback severity values.

View Source
const (
	StatusDraft     = "draft"
	StatusPublished = "published"
)

Feedback status values: draft (created locally, not yet filed) and published (filed as a GitHub issue, number + URL written by publish).

Variables

View Source
var ErrNotFound = errors.New("feedback not found")

ErrNotFound reports that no feedback file exists for the requested identity. It is the sentinel the CLI maps to the usage class (exit 2, unknown id).

Functions

func SetIssueAPIURL

func SetIssueAPIURL(url string)

SetIssueAPIURL overrides the issue-creation endpoint. Exported for tests (the cmd package end-to-end tests point the publish flow at an httptest server); production callers never touch it.

func SetIssueToken

func SetIssueToken(t string)

SetIssueToken sets the bundled issue token. It is exported for two callers: the ldflags injection path (see issueToken) and tests, which point the publish flow at an httptest server with a fake token.

Types

type APIError

type APIError struct {
	Status  int
	Message string
}

APIError is a non-2xx GitHub API response: the HTTP status and the API's message (e.g. "Bad credentials" on 401). The CLI maps it to the refusal class (exit 1).

func (*APIError) Error

func (e *APIError) Error() string

type Feedback

type Feedback struct {
	ID          string
	Type        string
	Title       string
	Severity    string
	Source      string
	EkaVersion  string
	OS          string
	Command     string
	Status      string
	IssueURL    string
	IssueNumber int
	Created     string
	Body        string
}

Feedback is one local feedback report: the triage record (ADR-026 §Decision 1) plus the markdown body that becomes the GitHub issue body. Created is the report date (YYYY-MM-DD); issue_number and issue_url are written by publish.

func Parse

func Parse(data []byte) (*Feedback, error)

Parse reads a feedback file (YAML frontmatter + markdown body) back into a Feedback. The body is everything after the closing "---" delimiter line, trailing newline preserved.

func Publish

func Publish(ctx context.Context, home, id string) (*Feedback, error)

Publish files a feedback draft as a GitHub issue on the fixed target repository and rewrites the local file with status: published plus the issue number and URL (ADR-026 §Decision 2/3).

Refusals are deterministic:

  • a missing id propagates ErrNotFound (the CLI maps it to the usage class, exit 2)
  • an already-published feedback refuses (idempotent — a second publish must never create a duplicate issue)
  • an empty token refuses with the release-binary hint (dev/test/CI builds ship no token — the ADR-024 version == "dev" analogue)
  • network and API failures refuse with the transport/APIError wrapped message (the CLI maps the APIError class to exit 1)

Returns the updated feedback (status published, number + URL set).

func (*Feedback) IssueBody

func (f *Feedback) IssueBody() string

IssueBody renders the GitHub issue body of the report (ADR-026 §Decision 4): the markdown report header built from the triage fields — type, severity, source, eka_version, os, command — then a blank line, then the feedback markdown body. Version/OS/command metadata is what makes a one-line agent report triageable.

func (*Feedback) Marshal

func (f *Feedback) Marshal() ([]byte, error)

Marshal renders the feedback file: the YAML frontmatter block (delimited by "---" lines) followed by the markdown body. The body always ends with exactly one trailing newline, so the serialization is a stable roundtrip (Parse(Marshal(f)) == f).

func (*Feedback) Validate

func (f *Feedback) Validate() error

Validate enforces the closed value sets of the triage record: type, severity and status must be valid values. Title must be non-empty (the create path requires it; a hand-edited file without one must not silently publish).

type IssueClient

type IssueClient struct {
	// Token is the fine-grained PAT (issues: write only).
	Token string
	// HTTP is the transport; production uses the package default
	// (issueClient), tests inject their own client when needed.
	HTTP *http.Client
}

IssueClient creates GitHub issues on the fixed target repository with the bundled token.

func (*IssueClient) CreateIssue

func (c *IssueClient) CreateIssue(ctx context.Context, title, body string) (number int, htmlURL string, err error)

CreateIssue creates one GitHub issue on the fixed target repository and returns its number and html_url. Any non-2xx response surfaces as *APIError carrying the status and the API's message. Network and transport failures are returned unwrapped.

type Store

type Store struct {
	// Dir is the feedback directory, e.g. <home>/feedback.
	Dir string
}

Store is the feedback directory: Dir = <home>/feedback (ADR-026 §Decision 1 — home-area storage, repository-independent). Feedback lives outside any repository and never enters the canonical store.

func New

func New(home string) *Store

New returns the feedback store rooted under home (the EKA workspace home, e.g. $EKA_HOME or ~/.eka).

func (*Store) List

func (s *Store) List() ([]*Feedback, error)

List returns all local feedback, sorted by id descending (newest first — ids embed YYYYMMDD). Deterministic and honest: the first malformed file fails the whole list with an error naming the file — a silent skip would hide a broken report.

func (*Store) Load

func (s *Store) Load(id string) (*Feedback, error)

Load reads <Dir>/<id>.md. Ids without the ".md" suffix are accepted. A missing file returns ErrNotFound. The id is validated before joining (it is user input on the publish path — an id carrying path separators must never escape the feedback directory).

func (*Store) MarkPublished

func (s *Store) MarkPublished(id string, issueNumber int, issueURL string) error

MarkPublished rewrites <id>.md atomically (write <id>.md.tmp then os.Rename) with status: published and the issue number + URL written by publish. A missing or already-published feedback is refused.

func (*Store) NewID

func (s *Store) NewID(title string, created time.Time) string

NewID derives the next free feedback identity: fbk-<YYYYMMDD>-<slug>. The slug is the lowercase title with every non-alphanumeric rune collapsed to "-" and edges trimmed; an empty slug falls back to "untitled". When the base identity already exists as a file, the suffix -2, -3, ... is appended until a free identity is found (collisions are possible within one day and one slug).

func (*Store) Save

func (s *Store) Save(f *Feedback) error

Save writes <Dir>/<f.ID>.md. The directory is created with 0700 permissions when missing (mirroring the workspace security posture: the reports are private to the user); the file itself is written 0600. A malformed feedback is refused before any write.

Jump to

Keyboard shortcuts

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