60 lines
2.8 KiB
Go
60 lines
2.8 KiB
Go
package ingest
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"textmachine/platform/internal/money"
|
|
)
|
|
|
|
// StatusReport is the ALLOWLISTED subset of `tmctl status --json` (pipeline.StatusReport) that the
|
|
// platform materializes. Everything absent here is absent on purpose: snapshot ids, drift, rebill,
|
|
// routing, content labels and the operator's flag taxonomy are engine vocabulary that must not
|
|
// cross the seam (contract §2.12), and unknown JSON fields are simply ignored by encoding/json.
|
|
//
|
|
// ⚠ Limit worth knowing: status has no PHASE split (engine backlog row 99). A resync can therefore
|
|
// restore the aggregate counter but not "draft N/M ∥ edit N/M"; the phase split lives only in the
|
|
// stream until row 99 lands. A reconciled run shows the aggregate until its next progress event.
|
|
type StatusReport struct {
|
|
BookID string `json:"book_id"`
|
|
TotalUnits int `json:"total_units"`
|
|
Done int `json:"done"`
|
|
InProgress int `json:"in_progress"`
|
|
Flagged int `json:"flagged"`
|
|
Pending int `json:"pending"`
|
|
// ETASeconds is what the owner asked to show (K-5); the engine already computes it.
|
|
ETASeconds float64 `json:"eta_seconds"`
|
|
// UnsignedBankTerms backs the signing screen's "N of M decided" while a stop is standing.
|
|
UnsignedBankTerms int `json:"unsigned_bank_terms"`
|
|
// Spend is the engine's committed spend, converted to integer micro-USD AT THE SEAM. The wire
|
|
// value is a JSON decimal; binding it to a float64 would put drift one step before the integer
|
|
// column that exists to prevent drift (PD-15). It is stored in the credit tables and NEVER
|
|
// projected into an API response or an INFO log (D39.84).
|
|
// A POINTER: absent, null and empty must not read as "the attempt cost nothing". A settlement
|
|
// computed from a missing figure would release the whole hold and charge zero.
|
|
Spend *money.MicroUSD `json:"committed_usd"`
|
|
Chapters []ChapterStatus `json:"chapters"`
|
|
}
|
|
|
|
// ChapterStatus is the per-chapter passport, allowlisted the same way (no cost, no verdict ranks).
|
|
//
|
|
// worst_flag_reason is deliberately NOT taken: contract v0 gives a chapter a note_count and nothing
|
|
// about the worst reason, so materializing it would store engine vocabulary no reader asks for
|
|
// (PD-19). It comes back with a column the day the chapter screen needs it.
|
|
type ChapterStatus struct {
|
|
Chapter int `json:"chapter"`
|
|
UnitsTotal int `json:"units_total"`
|
|
UnitsDone int `json:"units_done"`
|
|
UnitsFlagged int `json:"units_flagged"`
|
|
UnitsInProgress int `json:"units_in_progress"`
|
|
UnitsPending int `json:"units_pending"`
|
|
}
|
|
|
|
// DecodeStatus parses a status report.
|
|
func DecodeStatus(b []byte) (StatusReport, error) {
|
|
var r StatusReport
|
|
if err := json.Unmarshal(b, &r); err != nil {
|
|
return StatusReport{}, fmt.Errorf("ingest: decode status: %w", err)
|
|
}
|
|
return r, nil
|
|
}
|