Documentation
¶
Overview ¶
Package domain, glossary.go owns the canonical kendo-term dictionary used by the Preact <Term> tooltip component (web-mobile/js/glossary.jsx) AND by API error responses (handlers_*.go ResolveReasonHuman).
The source of truth is specs/003-tournament-gap-closure/glossary.md. When entries are added or tooltip wording changes, update both this file AND the spec. A unit test (glossary_test.go) parses glossary.md and asserts the Go map covers every term in it, so a forgotten entry fails the build.
The JS side reads the same data via web-mobile/js/glossary.js, which is generated from this file by `go generate ./internal/domain/...` (see the go:generate directive below). Running `make go/build` regenerates it; CI catches drift.
Package domain defines the core domain models for the bracket creator
Index ¶
- Variables
- func AssignSeeds(players []Player, assignments []SeedAssignment) error
- func IsKikenDecision(d Decision) bool
- func IsKikenDecisionStr(s string) bool
- func ResolveReasonHuman(reason string) string
- func ValidateAssignments(assignments []SeedAssignment) error
- type CompetitorStatus
- type Decision
- type EnchoMetadata
- type Match
- type MatchResult
- type MatchWinner
- type Player
- type Pool
- type Position
- type SeedAssignment
- type Side
- type TeamLineup
- type Term
- type Tournament
Constants ¶
This section is empty.
Variables ¶
var ( ErrCompetitorStatusMissingPlayerID = errors.New("competitor_status: playerId must be set") ErrCompetitorStatusMissingReason = errors.New("competitor_status: reason required when not eligible") )
Sentinel errors so callers can switch on the validation failure without parsing strings.
var ( ErrLineupMissingSenpo = errors.New("team_lineup: senpo must be present") ErrLineupMissingTaisho = errors.New("team_lineup: taisho must be present") ErrLineupTooManyMissing = errors.New("team_lineup: 3+ missing positions, team is disqualified") ErrLineupTeamSizeInvalid = errors.New("team_lineup: teamSize must be positive") )
var Glossary = map[string]Term{ "hikiwake": { ID: "hikiwake", Kanji: "引き分け", Short: "Draw", Tooltip: "The match ended tied, both competitors finished with equal points and no winner is declared. Common in pool matches; knockout matches go to overtime (encho) instead.", SeeAlso: []string{"encho"}, }, "encho": { ID: "encho", Kanji: "延長", Short: "Overtime", Tooltip: "Overtime, when a knockout match is tied at the end of regulation, an extra period is played until someone scores. Scoring in Encho follows ippon-shobu rules: first to one point wins.", SeeAlso: []string{"ippon-shobu", "ippon"}, }, "kiken": { ID: "kiken", Kanji: "棄権", Short: "Withdrawal", Tooltip: "The competitor withdraws. Two sub-types: Voluntary (FIK Art. 31), permanent disqualification from the competition; Injury (FIK Art. 30), can be reinstated if a doctor and shinpan permit it. The opponent wins 2–0 by default.", }, "kiken-voluntary": { ID: "kiken-voluntary", Kanji: "棄権", Short: "Voluntary withdrawal", Tooltip: "FIK Article 31, the competitor voluntarily withdraws. They forfeit this match (opponent wins 2–0) and are permanently barred from all later matches in this competition.", }, "kiken-injury": { ID: "kiken-injury", Kanji: "棄権", Short: "Injury withdrawal", Tooltip: "FIK Article 30, the competitor withdraws due to injury or illness. They forfeit this match (opponent wins 2–0) and are blocked from later matches, but can be reinstated if a doctor and shinpan-in approve.", }, "fusenpai": { ID: "fusenpai", Kanji: "不戦敗", Short: "No-show forfeit", Tooltip: "The competitor didn't appear when called to court. The opponent wins 2–0 by default. (Literal meaning: no-fight loss.)", }, "fusensho": { ID: "fusensho", Kanji: "不戦勝", Short: "Bye-win", Tooltip: "In a team match, when one team is short a player for a bout, the present opponent wins that bout 2–0 without fighting. (Literal meaning: no-fight win.)", }, "daihyosen": { ID: "daihyosen", Kanji: "代表選", Short: "Representative bout", Tooltip: "Tiebreaker for team knockout matches. When two teams finish equal on both individual wins and points, each picks one player to fight a single-point bout (ippon-shobu) to decide the match.", SeeAlso: []string{"ippon-shobu", "ippon"}, }, "hansoku": { ID: "hansoku", Kanji: "反則", Short: "Penalty", Tooltip: "A foul. Two hansoku awarded to a competitor give the opponent one free point.", }, "shiro": { ID: "shiro", Kanji: "白", Short: "White", Tooltip: "The competitor wearing a white ribbon at the back. Always shown on the left in this app.", }, "aka": { ID: "aka", Kanji: "赤", Short: "Red", Tooltip: "The competitor wearing a red ribbon at the back. Always shown on the right in this app.", }, "shiaijo": { ID: "shiaijo", Kanji: "試合場", Short: "Court", Tooltip: "The marked floor area where matches are fought. Labelled A, B, C, etc.", }, "ippon": { ID: "ippon", Kanji: "一本", Short: "Point", Tooltip: "A valid point, a clean strike with correct form, posture, and spirit. First competitor to two ippon wins the match (or one in overtime).", }, "ippon-shobu": { ID: "ippon-shobu", Kanji: "一本勝負", Short: "Single-point match", Tooltip: "First competitor to score one ippon wins. Used in overtime (encho) and tiebreaker (daihyosen) bouts.", SeeAlso: []string{"ippon", "encho", "daihyosen"}, }, "kachinuki": { ID: "kachinuki", Kanji: "勝ち抜き", Short: "Winner-stays-on", Tooltip: "A team format where the winner of each bout stays on to face the next opponent. A draw retires both. The match ends when one team has no more players left.", }, "senpo": { ID: "senpo", Kanji: "先鋒", Short: "First player", Tooltip: "The team's first player, fights bout 1 in a 5-person team. Senpo must be filled; this position cannot be left vacant, even when a teammate is withdrawn.", }, "jiho": { ID: "jiho", Kanji: "次鋒", Short: "Second player", Tooltip: "The team's second player, fights bout 2. If a team is short one player, FIK rules require that the Jiho position is the one left vacant.", }, "chuken": { ID: "chuken", Kanji: "中堅", Short: "Middle player", Tooltip: "The team's middle player, fights bout 3 in a 5-person team.", }, "fukusho": { ID: "fukusho", Kanji: "副将", Short: "Vice-captain", Tooltip: "The team's vice-captain, fights bout 4. If a team is short two players, FIK rules require jiho and Fukusho to be the two vacant positions.", SeeAlso: []string{"jiho"}, }, "taisho": { ID: "taisho", Kanji: "大将", Short: "Captain", Tooltip: "The team's captain, fights the final bout (bout 5). Like senpo, Taisho must be filled; this position cannot be left vacant.", SeeAlso: []string{"senpo"}, }, "zekken": { ID: "zekken", Kanji: "ゼッケン", Short: "Name tag (nafuda)", Tooltip: "The name tag affixed to the centre of the tare (waist protector). Also called nafuda. The display can show this name (instead of the registered name) so spectators see what's physically visible.", }, "dan": { ID: "dan", Kanji: "段", Short: "Rank", Tooltip: "A competitor's grade, shodan (1st), nidan (2nd), sandan (3rd), up to 8th. Optional in the entry form.", }, "dojo": { ID: "dojo", Kanji: "道場", Short: "Club or school", Tooltip: "The training school or club a competitor represents. The pool generator avoids placing two competitors from the same dojo in the same pool when it can.", }, "waza": { ID: "waza", Kanji: "技", Short: "Technique", Tooltip: "The specific type of strike scored, for example, men, kote, or do. Recorded with each ippon for the match record.", SeeAlso: []string{"ippon"}, }, "kachinuki-exhaustion": { ID: "kachinuki-exhaustion", Kanji: "", Short: "Team exhausted", Tooltip: "A kachinuki match ended because one team has no remaining players to send. The opposing team wins.", SeeAlso: []string{"kachinuki"}, }, "sune": { ID: "sune", Kanji: "すね", Short: "Shin strike", Tooltip: "A strike to the shin. Scores a point in Naginata competitions. Not a valid scoring technique in Kendo.", }, }
Glossary is the full term map keyed by lowercase romaji ID. The entries here mirror specs/003-tournament-gap-closure/glossary.md verbatim, tooltips are copied as-is so the spec and code never drift. Don't paraphrase.
Functions ¶
func AssignSeeds ¶
func AssignSeeds(players []Player, assignments []SeedAssignment) error
AssignSeeds applies valid seed assignments to a list of players It swaps seeds if a collision occurs. Returns error if a seeded participant is not found.
func IsKikenDecision ¶ added in v0.16.0
IsKikenDecision reports whether d is any kiken variant (legacy, voluntary, or injury). Use this instead of comparing against DecisionKiken alone, the legacy value is kept for backward compatibility but new code should use the specific sub-types.
func IsKikenDecisionStr ¶ added in v0.16.0
IsKikenDecisionStr is the string-argument twin of IsKikenDecision, for call sites that hold the wire value as a string (e.g. MatchResult.Decision).
func ResolveReasonHuman ¶ added in v0.16.0
ResolveReasonHuman returns the human-friendly gloss for an engine-emitted reason string, or "" when no pattern matches.
Callers wire this into error responses alongside the raw `reason` field so UIs can show "withdrew from match m_12" instead of "kiken at m_12". Returning "" rather than echoing the input lets the caller decide whether to omit the field or fall back to the raw reason, the JSON omitempty tag on ReasonHuman handles the typical case.
func ValidateAssignments ¶
func ValidateAssignments(assignments []SeedAssignment) error
ValidateAssignments checks a list for duplicate seed ranks, valid properties, and gapless sequences.
Types ¶
type CompetitorStatus ¶ added in v0.16.0
type CompetitorStatus struct {
PlayerID string `json:"playerId" yaml:"playerId"`
Eligible bool `json:"eligible" yaml:"eligible"`
Reinstateable bool `json:"reinstateable,omitempty" yaml:"reinstateable,omitempty"`
Reason string `json:"reason,omitempty" yaml:"reason,omitempty"`
MatchID string `json:"matchId,omitempty" yaml:"matchId,omitempty"`
RecordedAt time.Time `json:"recordedAt" yaml:"recordedAt"`
}
CompetitorStatus tracks whether a player is eligible to compete after a kiken/fusenpai withdrawal recorded earlier in the tournament.
FR-034, data-model §3.
func (CompetitorStatus) Validate ¶ added in v0.16.0
func (c CompetitorStatus) Validate() error
Validate enforces FR-034: PlayerID must be set, and an ineligible status must carry a non-empty Reason.
type Decision ¶ added in v0.16.0
type Decision string
Decision identifies how a match was concluded.
FR-030, NFR-011, FR-044, see data-model §1.
const ( DecisionNone Decision = "" DecisionFought Decision = "fought" DecisionHikiwake Decision = "hikiwake" DecisionKiken Decision = "kiken" DecisionKikenVoluntary Decision = "kiken-voluntary" DecisionKikenInjury Decision = "kiken-injury" DecisionFusenpai Decision = "fusenpai" DecisionFusensho Decision = "fusensho" DecisionDaihyosen Decision = "daihyosen" DecisionKachinukiExhaustion Decision = "kachinuki-exhaustion" DecisionIpponShobu Decision = "ippon-shobu" )
func (*Decision) UnmarshalYAML ¶ added in v0.16.0
UnmarshalYAML migrates legacy `decision` values (NFR-025, R6):
- bool true → DecisionHikiwake (the historical "draw" flag)
- bool false → DecisionFought (legacy YAML only persisted the field on completed matches; "not a draw" meant the match was fought to a result)
- any defined string wire value → that Decision
- any other string → DecisionNone (schema-tolerant load)
type EnchoMetadata ¶ added in v0.16.0
type EnchoMetadata struct {
PeriodCount int `json:"periodCount" yaml:"periodCount"`
}
EnchoMetadata records overtime (encho) periods played to resolve a tied match.
type MatchResult ¶ added in v0.16.0
type MatchResult struct {
Decision Decision `json:"decision,omitempty" yaml:"decision,omitempty"`
DecisionBy Side `json:"decisionBy,omitempty" yaml:"decisionBy,omitempty"`
DecisionReason string `json:"decisionReason,omitempty" yaml:"decisionReason,omitempty"`
Encho *EnchoMetadata `json:"encho,omitempty" yaml:"encho,omitempty"`
}
MatchResult captures the outcome metadata of a match.
FR-030, FR-032, data-model §2.
type MatchWinner ¶
MatchWinner represents a player who has won a match
type Player ¶
type Player struct {
ID string `json:"id,omitempty"` // stable UUID assigned at first persist
Name string `json:"name"`
DisplayName string `json:"displayName"`
Dojo string `json:"dojo"`
Metadata []string `json:"metadata,omitempty"`
Source string `json:"source,omitempty"` // registration source, how the entry was created: registered (self-registered), manual (operator-added), transfer (imported)
CheckedIn bool `json:"checkedIn"`
PoolPosition int64 `json:"-"` // internal: used for Excel output and pool-draw ordering; not serialised to JSON because the value is inconsistently indexed across producer paths (0-based in handlers, 1-based in helper). Draw order on the wire is conveyed by pool.players array ordering.
Seed int `json:"seed"`
// Number is the player's "tag", their assigned competitor number / on-court
// identifier (zekken-style), optionally prefixed via the competition's
// numberPrefix (e.g. "A1"). This is distinct from the registration Source field.
Number string `json:"number,omitempty"` // e.g. "K1", assigned when --number-prefix is set
}
Player represents a tournament participant. It is the canonical participant type across state, engine, mobileapp, and helper packages; internal/helper re-exports it under helper.Player as a type alias for rendering-side ergonomics (NFR-007).
type Position ¶ added in v0.16.0
type Position string
Position names a slot in a team lineup. For 5-person teams the named FIK constants apply; for other sizes positions are numeric strings "1".."N" produced by PositionNumbered.
FR-040, data-model §4.
func PositionNumbered ¶ added in v0.16.0
PositionNumbered returns the canonical Position value for a non-5 team size, where positions are 1-indexed numeric strings.
type SeedAssignment ¶
type SeedAssignment struct {
Name string `json:"name"`
Dojo string `json:"dojo,omitempty"`
SeedRank int `json:"seedRank"`
}
SeedAssignment represents the mapping of a previous winner to a seed position.
func (*SeedAssignment) Validate ¶
func (s *SeedAssignment) Validate() error
Validate checks if the seed assignment is valid.
type Side ¶ added in v0.16.0
type Side string
Side identifies which side of a match (Shiro = white, Aka = red).
FR-032, data-model §2.
type TeamLineup ¶ added in v0.16.0
type TeamLineup struct {
TeamID string `json:"teamId" yaml:"teamId"`
CompetitionID string `json:"competitionId" yaml:"competitionId"`
Round int `json:"round" yaml:"round"`
MatchID string `json:"matchId,omitempty" yaml:"matchId,omitempty"`
Positions map[Position]string `json:"positions" yaml:"positions"`
LockedAt *time.Time `json:"lockedAt,omitempty" yaml:"lockedAt,omitempty"`
// ChangeReason is a mandatory audit justification when an operator
// edits a lineup after the match has started (force=true). Format:
// "<category>: <note>" (e.g. "Substitution: injury to jiho").
// Omitted for pre-match lineup submissions.
ChangeReason string `json:"changeReason,omitempty" yaml:"changeReason,omitempty"`
}
TeamLineup pins which player occupies each Position for a team in a given round OR for a specific match. The lineup is replaceable up until its match (match-scoped) or its round's first match (round-scoped) starts, at which point LockedAt is set and further PUT/PATCH operations are rejected.
Keying (mp-825): when MatchID is non-empty the lineup is match-scoped, a team may field a different order/roster for each encounter (e.g. successive pool matches), and each entry locks independently when its own match starts. When MatchID is empty the lineup is round-scoped (the legacy behavior, still used by bracket rounds and pre-mp-825 data): one lineup per (team, round), frozen when the round's first match starts. The two scopes coexist; a match-scoped entry shadows the round-scoped fallback for that match.
FR-040, data-model §4.
func (TeamLineup) Validate ¶ added in v0.16.0
func (t TeamLineup) Validate(teamSize int) error
Validate enforces FR-037 / FR-041 / R4 / CHK012: a 5-person lineup must include Senpo and Taisho, and any kiken vacancies must follow the FIK back-fill rule (the missing position is Jiho first, then Jiho+Fukusho). For non-5 sizes positions must be numeric "1".."N".
Returning a non-nil error signals the team should not be allowed to take the court (either reject the lineup PUT or DQ the team via CompetitorStatus).
func (TeamLineup) ValidatePositions ¶ added in v0.17.0
func (t TeamLineup) ValidatePositions(teamSize int) error
ValidatePositions checks only that the position KEYS are valid for the team size; it does NOT enforce the FIK completeness/vacancy rule. Lineups are entered incrementally while bouts run, so a partial lineup must be persistable, completeness is surfaced as a non-blocking UI warning, not enforced at write time.
type Term ¶ added in v0.16.0
type Term struct {
ID string `json:"id"`
Kanji string `json:"kanji"`
Short string `json:"short"`
Tooltip string `json:"tooltip"`
SeeAlso []string `json:"seeAlso,omitempty"`
}
Term is a single dictionary entry. ID is the lowercase romaji ("kiken", "ippon-shobu", "kachinuki-exhaustion"); it doubles as the map key and as the `name` prop of the JSX <Term> component. Kanji is the Japanese spelling (or empty for the few entries without one, e.g. "kachinuki-exhaustion"). Short is the one-word English gloss the /glossary page renders next to the term. Tooltip is the full volunteer-facing sentence (rendered inside the popover and the /glossary page). SeeAlso lists the IDs of cross-referenced terms; the JSX component uses it to render nested <Term> spans inside the tooltip so hovered cross-references reveal their own tooltips.
type Tournament ¶
Tournament represents the complete tournament structure
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
glossarygen
command
Package main, glossarygen emits web-mobile/{js,dist}/glossary_data.js from internal/domain/glossary.go (the Go-side dictionary).
|
Package main, glossarygen emits web-mobile/{js,dist}/glossary_data.js from internal/domain/glossary.go (the Go-side dictionary). |