Skip to content

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

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions