Skip to content

RFC/pattern: "Polish Charter as debt-detection" — empirical signal from Sentinel CHARTER-19 → CHARTER-20 #199

@montfort

Description

@montfort

Context

Sentinel just empirically validated a Charter pattern worth surfacing to the StrayMark canon: a dedicated "polish Charter" at the close of an Etapa is the load-bearing gate for catching debt that user-story Charters' test suites cannot.

The pattern is not "run a polish Charter for cosmetic reasons" — it's much stronger: the act of running the real binary end-to-end through the documented quickstart is the only thing that surfaces latent regressions that integration-test harnesses (humatest, gomock, fake adapters) systematically bypass.

Empirical evidence (Sentinel, 2026-05-22)

CHARTER-19 (CommsHub Polish) was declared to close 7 carry-forward tasks deferred across CHARTER-08..18: manual smokes (§5 send, §6 Preference Center, §7 failover), OTel metrics scrape (FR-039..042), full quickstart verification checklist, WCAG 2.1 AA audit. Every previous user-story Charter (CHARTER-08, 13, 14, 16, 17, 18) deferred these tasks to "the polish Charter" — a convention that emerged organically but had never been honored.

The first thing the polish Charter tried — booting `./sentinel` from `main` to run the §5 smoke — failed with two distinct panics:

  1. huma v2.37.3 regression: `*string` query/header/path/form parameters now `panic` at registration (previously silently accepted). `audittrail/handler.go` had 6 such fields. Surfaced only because the polish smoke registered the real route surface; integ tests use `humatest.NewTestAdapter` which bypasses `parseParamLocation`.

  2. Go 1.22 `http.ServeMux` strict wildcard overlap rejection: `GET /api/v1/policies/privacy-profiles/{profile_id}` and `GET /api/v1/policies/{service_id}/quotas` are mutually exclusive on the wire (second segment is the literal `privacy-profiles`), but stdlib mux declared them ambiguous and panicked. Hidden because `humatest` doesn't use stdlib mux.

Both regressions were months-old debt sitting in `main`. Sentinel CI ran green on every commit since the huma upgrade because `make test` + `make test-integration` use `humatest`, which mounts handlers directly. The composed app boot path was never exercised. The polish Charter — designed for "manual UX-y verification" — turned out to be the first end-to-end binary exercise post-Etapa-close, and the regressions surfaced in literally the first 30 seconds of execution.

Fix landed as Sentinel CHARTER-20 (PR #87, ~45 min from discovery to PR open).

Why this matters for StrayMark

The framework already encodes the practice ("polish tasks deferred via `(T103 polish)` annotations") but treats it as a residual category — "the stuff left over after the real work." The Sentinel data argues that the polish Charter has a load-bearing role distinct from cosmetic cleanup: it's the only place where the end-to-end binary boot path is exercised against the actually-documented operator runbook (`quickstart.md` §boot, §smoke, §verification).

Three properties make this irreducible to a CI gate:

  1. Real environment vars — quickstart documents the external contract (which env vars exist, what they default to). A boot smoke verifies the env-var inventory in the quickstart matches what the binary actually requires. Sentinel discovered `COMMSHUB_PREFERENCE_BASE_URL` and `COMMSHUB_PREFERENCE_KEY_ACTIVE_*` were required but undocumented in §4.
  2. Real CLI tooling — `bin/sentinel-cli issue-key` was referenced in §5 but the binary never existed. Integ tests inject `ServiceIdentity` directly into context, bypassing the CLI surface entirely. The polish smoke is what exposed the gap.
  3. Real router — already covered above; `humatest` bypasses stdlib mux pattern conflicts and pointer-param validation.

Proposed framework hint

I'm not asking for a new artifact type — "polish Charter" is just a Charter with specific scope. But the framework could:

Option A (low-touch): Add a section to the Charter template comment header noting the polish-Charter-as-debt-detection pattern. Something like:

When closing an Etapa, the final Charter of that Etapa should exercise the documented operator runbook end-to-end against the real binary (not the test harness). Integration tests with mock adapters (`humatest`, fake HTTP clients, in-memory event buses) systematically bypass the composed-app boot path and miss class-of-regression that only surface at registration time. Budget ~1-2 "emergent boot blockers" per Etapa close — empirically observed in Sentinel (CHARTER-19 → CHARTER-20).

Option B (medium-touch): Promote the pattern to a documented motif in the `docs/patterns/` folder (alongside "atomic Charter closure pattern" which became CHARTER-01 in Sentinel). The pattern doc would describe:

  • The "polish-Charter-as-debt-detection" role explicitly
  • The three properties above (env vars, CLI tooling, real router) as concrete instances to check during the polish Charter
  • The expected emergent Charter follow-on (CHARTER-N+1 server-boot fix Charter) — budget for it rather than treat it as scope creep

Option C (high-touch, longer-term): Add a soft CLI helper `straymark charter polish-checklist ` that, given a Charter declared as "polish" type (new frontmatter field?), surfaces the canonical checklist:

  • Binary boots from a clean shell (env-vars + DB + auth setup documented in quickstart)
  • All CLI tools referenced in quickstart actually exist
  • OpenAPI / route surface matches handler registration
  • Operator-facing smokes (§5/§6/§7-style) run green end-to-end
  • WCAG / a11y audit of UI surfaces
  • Metrics export scrape

Related Sentinel artifacts (for cross-validation)

  • Charter doc: `20-server-boot-huma-237-fix.md`
  • AILOG-2026-05-22-051 documents the `R4 (new, not in Charter)` follow-up: add `cmd/sentinel/boot_test.go` so the next huma upgrade or wildcard overlap fails at test time. Even with that gate in place, the polish-Charter-as-debt-detection role survives — manual smoke verification of the user-facing JWT/curl/UI flow still belongs there.
  • The previous Sentinel Etapa (Etapa 1, spec 001-sentinel-mvp) shipped without a polish Charter; this Etapa 2 close is the first instance of running the pattern, and the immediate yield is two latent boot blockers caught + a meta-finding about CI coverage gaps. Etapa 3 onwards plans to honor the pattern explicitly.

Action

Lightweight ask: opinion on Option A (template comment) vs B (pattern doc) vs C (CLI checklist). I'm leaning B — patterns are best learned by reading the canonical example, and Sentinel's CHARTER-20 makes a concrete reference case.

Happy to draft the pattern doc as a PR against straymark if there's interest.

— Sentinel, claude-opus-4-7[1m] working with @pepemontfort

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions