Project a debt ledger from canonical history, read-only
Slice one of DEBT-DESIGN.md, with the four amendments applied. It writes nothing: no new event types, no scheduling, no clustering, no maintenance tasks. The point is to find out whether the model can represent debt this project already knows about, before committing to a durable schema. The projected type lives in domain and the fold lives in store, so the first implementation does not bake a read model into the command layer. operations owns the one action that exists, CheckDebtEligibility, which is a pure function returning explicit reasons like CheckSubmission. Signatures carry their version in the string. Normalization rules will change, and without a version that silently regroups history and moves the recurrence counts eligibility was already decided on. Observations require exactly one of event_id and legacy_ref. Imported Fxx history predates the events that would justify it, and a fabricated event id would break the provenance rule the ledger exists to enforce. Incompleteness is reported, not hidden. Manual interventions and worker observations are carried by no event type, so the ledger names both as non-durable gaps rather than reading as "no operational cost". The operational refusal reason says the intervention count is structurally zero on every current log. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CVbaKucEYBjMqVeUgJUsc1
This commit is contained in:
+58
-11
@@ -98,7 +98,9 @@ No new subsystem. Two event types on the existing spine, one projection beside
|
||||
// DebtObservation is one piece of counted evidence, always pointing at the
|
||||
// event that produced it. Provenance is an event id, never prose.
|
||||
type DebtObservation struct {
|
||||
EventID string `json:"event_id"`
|
||||
// Exactly one of EventID and LegacyRef. See section 8.
|
||||
EventID string `json:"event_id,omitempty"`
|
||||
LegacyRef string `json:"legacy_ref,omitempty"`
|
||||
TaskID string `json:"task_id,omitempty"`
|
||||
Kind string `json:"kind"` // block_reason, failure_class, review_finding,
|
||||
// plan_mismatch, manual_intervention, deferred_finding
|
||||
@@ -152,6 +154,18 @@ structural class + finding severity + normalized component
|
||||
|
||||
Exact signature match attaches automatically. That is the only automatic path.
|
||||
|
||||
**Signatures are versioned, in the string itself.**
|
||||
|
||||
```text
|
||||
v1:operational:lease_not_renewed:workpc-opencode:federation
|
||||
```
|
||||
|
||||
Normalization rules will change. Without a version, changing them silently
|
||||
regroups every historical observation, and the recurrence counts that drive
|
||||
eligibility move underneath the items that already used them. A `v2` signature
|
||||
never matches a `v1` one, so old evidence keeps the grouping it was counted
|
||||
under.
|
||||
|
||||
**A model may propose, never merge.** Clustering emits `DebtMergeSuggested`
|
||||
carrying both item ids and its reasoning. The merge is an operator action or a
|
||||
policy threshold, recorded as `DebtItemUpdated`.
|
||||
@@ -245,6 +259,19 @@ One-time import, by hand, with a class assigned per row. Each becomes a
|
||||
`DebtItem` with `Status: repaired`, its commit as `RepairCommit`, and one
|
||||
observation citing the ledger.
|
||||
|
||||
**Imported observations carry legacy provenance, never a fabricated event id.**
|
||||
The Fxx history predates the capability that would have produced an event, and
|
||||
inventing one would break the provenance rule the ledger exists to enforce.
|
||||
|
||||
```go
|
||||
EventID string `json:"event_id,omitempty"`
|
||||
LegacyRef string `json:"legacy_ref,omitempty"` // "BURNIN.md:F18"
|
||||
```
|
||||
|
||||
Exactly one is required. A reader can then tell a counted fact from an imported
|
||||
claim at a glance, and the counts that drive eligibility can exclude imported
|
||||
evidence if that turns out to matter.
|
||||
|
||||
**Keep the namespace.** New items continue at F62. Two numbering schemes would
|
||||
be the first structural debt the ledger itself creates.
|
||||
|
||||
@@ -271,21 +298,41 @@ represent the history this project already has.
|
||||
schema commitment.**
|
||||
|
||||
```text
|
||||
internal/operations/debt.go DebtItem, signature, projection over s.Events(0)
|
||||
CheckDebtEligibility
|
||||
GET /v1/debt read-only, tui surface
|
||||
internal/domain/debt.go DebtItem, DebtObservation, signature, classification
|
||||
internal/store/debt_projection.go the fold over s.Events(0), and the gap report
|
||||
internal/operations/debt.go CheckDebtEligibility, and later the actions
|
||||
GET /v1/debt read-only, tui surface
|
||||
```
|
||||
|
||||
The proof is that the current log already contains runs 10 to 14. The projection
|
||||
must independently surface, from history alone:
|
||||
The projected type stays out of `operations`. Baking a read model into the
|
||||
command layer in the first slice is the mistake that would be hardest to undo
|
||||
later. `operations` owns actions: `CheckDebtEligibility` now, and
|
||||
`SuggestDebtMerge`, `PromoteDebtItem` and `ScheduleDebtRepair` when they exist.
|
||||
|
||||
- the release loop as operational debt, with its 409 recurrence and two manual
|
||||
cleanups
|
||||
- the opencode adapter gap as operational debt, across three failed attempts on
|
||||
one harness
|
||||
The proof is what the projection can and cannot recover from canonical history.
|
||||
Section 2 already says worker observations are not durable and operator
|
||||
interventions are unrecorded, so demanding their reconstruction would be asking
|
||||
the projection to invent evidence.
|
||||
|
||||
The first burn-in is therefore three requirements, not one:
|
||||
|
||||
1. Recover every debt signal that canonical history actually encodes.
|
||||
2. Report, explicitly and per kind, where known debt cannot be reconstructed.
|
||||
3. Never infer a missing observation from `BURNIN.md`.
|
||||
|
||||
Incompleteness is part of the result, not a failure. The 409 loop lived in the
|
||||
F18 worker ring rather than in an event, so the ledger should say it has no
|
||||
durable evidence for that shape. That statement is what makes slice two
|
||||
necessary, measurably rather than by assertion.
|
||||
|
||||
What the projection should recover from the log alone:
|
||||
|
||||
- repeated `lease_expired` and `retry_limit` on one harness, which is the
|
||||
opencode failure shape across four tasks
|
||||
- the retry-idleness dynamic, attached to the tasks that expired
|
||||
- review findings grouped by component, if any repeat
|
||||
|
||||
A model that cannot reproduce debt already known is wrong, and no schema has
|
||||
A model that cannot represent debt already known is wrong, and no schema has
|
||||
been committed to yet. That is the cheapest place to find out.
|
||||
|
||||
**Slice two** makes worker observations durable, because that is the input the
|
||||
|
||||
Reference in New Issue
Block a user