Skip to content

RFC: Pre-declare SpecKit refresh + post-close Batch 7.4 — two patterns surfaced in Sentinel CHARTER-18 #156

@montfort

Description

@montfort

RFC: Pre-declare SpecKit refresh + post-close audit-driven Batch 7.4 — two patterns surfaced in Sentinel CHARTER-18

Author: Claude Opus 4.7 (claude-code-v1.0) on behalf of StrangeDaysTech/sentinel adopter (operator José Villaseñor Montfort)
Source: StrangeDaysTech/sentinel CHARTER-18 (CommsHub US5 — Cloud Tasks failover + tracking + anomaly) end-of-chain retrospective
Date: 2026-05-15
Local RFC file: .straymark/06-evolution/charter-18-pattern-evolution-rfc.md (Sentinel PR filed in parallel)
Telemetry artifact: .straymark/charters/CHARTER-18.telemetry.yaml
Audit cycle: 7th cross-family pair (gemini-2.5-pro + gpt-5.3-codex)
Precedent: this RFC follows issue #111 — empirical observation in Sentinel → local pattern → upstream proposal with adoption path.

Resumen ejecutivo

CHARTER-18 was the seventh and final user-story Charter of the CommsHub module — a 7-Charter chain (CHARTER-08 → 13 → 14 → 15 → 16 → 17 → 18) where accumulated empirical learnings from prior charters had drifted the SpecKit artifacts (plan / data-model / contracts / quickstart / research) far enough that going straight from CHARTER-17 close into CHARTER-18 declare would have caused systematic mid-flight scope expansions. The adopter's response was to (1) interpose a pre-declare SpecKit refresh PR that integrated 14 categorized learnings + 15 empirical corrections + 3 operator decisions BEFORE Charter declare, and to (2) later apply a post-close audit-driven Batch 7.4 amendment when external audit cycle 7 surfaced findings that required code changes after status: closed.

Both patterns produced verifiable telemetry signals worth surfacing to StrayMark as candidate framework conventions. This issue tracks them for upstream consideration.

Pattern 1 — Pre-declare SpecKit refresh

Hallazgo

StrayMark's Charter pattern assumes the SpecKit artifacts (the contract surface a Charter declares scope against) are stable enough at declare-time to plan against. Over a 7-Charter chain in a single module, that assumption breaks empirically. By CHARTER-17 close, 14 categorized learnings had accumulated from CHARTER-08..17:

  • 7 reusable patterns (withRLS wrapper, cursor pagination tuple, brand-cache LRU, core.processed_events dedup, FR-005 PL/pgSQL trigger pattern, EnvSecretLoader, shared BrandCache).
  • 4 code gaps (commshub_anomaly_pauses unwired since CHARTER-07; Mailgun ParseWebhook = ErrUnsupported stub; Recipient.tenant_id column gap; webhook_dispatcher sync-with-retry placeholder).
  • 3 discipline patterns (cross-family audit pair; Batch 7.4 remediation; per-batch close discipline via straymark charter batch-complete).

Plus 15 empirical corrections (EC1..EC15) — places where the original spec drifted from the implementation (migration numbering, table naming conventions, framework version assumptions).

Without integration, the next Charter (CHARTER-18) would have re-discovered each pattern mid-flight, producing R<N> (new, not in Charter) entries documenting "we should have known this". The cost is not abstract — prior charters in the chain produced 5-10 emergent R<N> entries each, with the trend visibly rising.

Implementación (Sentinel-side)

Sentinel codified a dedicated refresh PR between CHARTER-17 close and CHARTER-18 declare:

  • PR: StrangeDaysTech/sentinel#76 (commit d1d7292, 2026-05-14): "spec(002-commshub): US5 plan refresh — LOCKED-aware Phase 7+8 redesign"
  • AIDEC: AIDEC-2026-05-14-001-speckit-plan-scope-limited-us5-refresh
  • Files touched: non-locked sections of specs/002-commshub/{plan,data-model,quickstart,research,spec}.md + contracts/*.md.

The refresh integrated the 14 prior-chain learnings into a categorized table in research.md, plus 15 empirical corrections, plus 3 operator decisions (D1 / D2 / D3) ratified pre-declare. CHARTER-18's ## Context section then explicitly cites each pattern + correction + decision by ID, so the Charter scope is grounded in the refreshed reality.

Telemetría — qué señales produjo

From CHARTER-18.telemetry.yaml:

Field Value Reading
pre_work.items_declared 5 Forward-pointers from CHARTER-17 to be consumed
pre_work.items_completed_before_planning 5 All closed pre-declare (refresh did the work)
pre_work.items_discovered_during_planning 0 Nothing surfaced mid-Charter
pre_work.pre_work_quality "high" Explicit operator rating
effort.estimation_drift_factor 1.0 On-budget across 10 batches
outcome.completed_as_planned true First Charter in the chain to close cleanly without a mid-flight remediation Charter
qualitative.overall_satisfaction 5 / 5 Adopter's qualitative score
qualitative.wins[1] "EC1..EC15 empirical-corrections inventory absorbed pre-execution risk into in-execution awareness. Five risks (EC4 idempotency, EC8 Mailgun ParseWebhook closure, EC15 channel_id PRIMARY KEY) would have been emergent R-entries in prior Charters; their pre-declaration in research.md eliminated mid-flight surprise." Direct evidence the pattern delivered

Operator's effort.estimation_drift_reason statement: "On budget. ... the SpecKit refresh from PR #76 ... eliminated most ambiguity that drove drift in prior Charters. No mid-flight remediation Charter required — the EC1..EC15 empirical-corrections inventory in research.md absorbed what would have been pre-execution risk into in-execution awareness."

Patrón propuesto para StrayMark

Name: Pre-declare SpecKit refresh (or: Empirical Corrections Inventory).

When it applies: a module with N ≥ 3 user-story charters where the average r_n_plus_one_emergent_count per charter has been rising AND the SpecKit artifacts were authored against the framework version at module start (not refreshed since).

Mechanics:

  1. Refresh PR before next Charter declare. Touches non-locked sections of plan.md, data-model.md, contracts/*, quickstart.md, research.md. Optional AIDEC documenting the refresh decision + alternatives considered.
  2. Categorized learnings table in research.md with at least these buckets: reusable patterns, code gaps, discipline patterns, empirical corrections.
  3. Operator decisions (Dn) listed explicitly with alternatives + chosen path + rationale — these are the contracts the next Charter inherits.
  4. Next Charter's ## Context cites each pattern + correction + decision by ID so scope grounding is explicit.

Trigger heuristic (could be a straymark charter refresh-suggest <module> CLI command):

  • IF module has ≥ 3 closed charters AND
  • IF the rolling mean agent_quality.r_n_plus_one_emergent_count of the last 3 charters is > some threshold (Sentinel's data suggests > 6) AND
  • IF no refresh PR has landed in this module since the last charter chain branch point
  • THEN suggest a refresh before declaring the next charter

Telemetry slot suggestion: add to charter_telemetry:

pre_declare_refresh:
  enabled: true | false
  refresh_pr: <url or null>
  refresh_aidec: <id or null>
  reusable_patterns_integrated: <count>
  code_gaps_integrated: <count>
  discipline_patterns_integrated: <count>
  empirical_corrections_integrated: <count>
  operator_decisions_ratified: <count>

so the framework can correlate pre_declare_refresh.enabled=true charters against effort.estimation_drift_factor + outcome.completed_as_planned across the StrayMark adopter dataset.

Pattern 2 — Post-close audit-driven Batch 7.4 amendment

Hallazgo

StrayMark v1 audit cycle docs the in-Charter "Batch 7.4 remediation pattern": when external audit findings emerge before close, apply them atomically pre-close in the same commit/PR. But what about findings that surface after status: closed? Audit cycles in Sentinel run post-close (the audit prompt is generated at close ceremony, auditors execute asynchronously, the operator runs /straymark-audit-review whenever both auditors have written their reports). For a Charter that closes 2026-05-15 with audit reports landing 2026-05-15..05-17, findings can arrive after the close ceremony.

The framework's options today:

  • (a) Open a new Charter for remediation. Heavy: full declare + Tasks + ceremony for what might be ~5 file edits.
  • (b) Surface findings in the audit review.md and leave them open. Loses the "atomic with the Charter" property.
  • (c) Bend the in-Charter Batch 7.4 pattern to a post-close amendment commit, which is technically what the in-Charter doc encourages ("Track each finding remediation as F in AILOG; track each new emergent risk as R<N+1> (new, not in Charter)").

Sentinel chose (c) for CHARTER-18 because the 5 findings (4 from gpt-5.3-codex, 1 from gemini-2.5-pro) were code-level fixes (not architectural reopens), the fix surface was cohesive (19 files, +2257/-106 lines) but bounded — a single PR landed atomically — and a new Charter would have created multi-week governance overhead for ~6h of focused engineering.

Implementación (Sentinel-side)

  • Branch: charter-18-execute (the original execute branch, still mergeable to main).
  • Commit: f8e135d"charter-18(batch-7.4): audit-driven remediation — DI wiring + worker retry + decommission filter + attempt-counter fix".
  • AILOG: AILOG-2026-05-15-050 (NEW, risk_level: high, review_required: true). The original AILOG-2026-05-14-049 was amended in §R16 with a "Historical correction" subsection pointing forward to 050.
  • PR: StrangeDaysTech/sentinel#78 (the original CHARTER-18 execute PR, augmented with the Batch 7.4 commit before merge).
  • Telemetry: the external_audit: YAML array in CHARTER-18.telemetry.yaml was populated via manual merge (the CLI --merge-into rejected — see sub-issue below).

Findings closed

Finding Severity Auditor Closure
F1 Critical gpt-5.3-codex (C1) DI wiring completo: extends wire.Struct(SenderDeps) with Providers + RetryEnqueuer; extends wire.Struct(ServiceDeps) with CloudTasks; chains WithCloudTasksEnqueuer / WithWebhookProviders / WithWorkerOIDC / WithSendRetrier in ProvideCommsHandler + ProvideWebhookDispatcher
F2 High gpt-5.3-codex (H1) Sender.RetryFromTask implements the failover-with-skip-previous-providers logic; runSendRetry replaces ACKNOWLEDGED_NOT_IMPLEMENTED_YET
F3 High gpt-5.3-codex (H2) runWebhookDispatch reads X-Cloudtasks-Taskretrycount header instead of hardcoded task.AttemptNumber=1
F4 High gpt-5.3-codex (H3) CancelServiceTasks filters by service_id via JSON-probe before DeleteTask, closing multi-tenant data-loss vector
F5 Medium (was Critical) gemini-2.5-pro (C1) Makefile adds explicit -timeout=$(INTEG_TIMEOUT) default 600s; recalibrated severity in the calibrator review
FX Medium calibrator (missed by all) AILOG-049 §R16 historical correction — wire promise was made in Batch 7 but never honored; closed in Batch 7.4

Plus a follow-up CI hardening PR (#79) adding an Integration Tests (testcontainers) job to ci.yml so FU-074's trigger is now mechanical (gate failure) rather than operator-reactive.

Sub-issue surfaced — CLI --merge-into rejects empty placeholder

When straymark charter close <id> runs at close ceremony, it writes a telemetry YAML with external_audit: [] (empty array placeholder). When the audit cycle completes later and /straymark-audit-review runs straymark charter audit <id> --merge-reports --merge-into <telemetry-yaml>, the CLI rejects with:

error: <telemetry-yaml> already has an `external_audit:` block.
Re-audit (appending to an existing array) is not supported in v0.
Re-run `straymark charter audit <id> --finalize` (without --merge-into)
to print the new YAML, then merge manually if you want to append.

The semantics make sense for the case "audit was already merged once, refusing to merge twice", but external_audit: [] (empty array) and external_audit: [<entries>] (non-empty) are not distinguished — the CLI rejects both. The fallback path (Branch B in the /straymark-audit-review skill) works, but it shifts the merge to manual operator edit, which then risks YAML indentation errors (the Sentinel operator hit one — external_audit: ended up at column 0 instead of column 2 inside charter_telemetry:, requiring a follow-up fix-up edit).

Recommendation: relax the v0 check to "already has a populated external_audit: array" so the placeholder case (empty array at close) doesn't block the post-close merge.

Patrón propuesto para StrayMark

Name: Post-close audit-driven amendment (or: Batch N.4 extended to post-close).

When it applies: external audit findings arrive after the Charter's status: closed but before sufficient time has passed that a new Charter is the more natural unit.

Mechanics:

  1. Same execute branch (do not branch off main). The Charter's original branch is mergeable to main; the amendment commit rides along.
  2. New AILOG (not amend the original — risk_level may have changed, audit decisions are distinct from execute decisions).
  3. Historical correction subsections in the original AILOG for any §R entries that the amendment proves were stale.
  4. PR comment on the original PR (do not open a new PR if the execute branch hasn't merged yet) describing the amendment + closed findings.
  5. Telemetry update: the external_audit: array gets populated (manual merge if CLI rejects per the sub-issue above).

Trigger heuristic:

  • IF Charter status: closed AND
  • IF audit findings emerge in review.md that are graded Critical or High AND
  • IF the closure_criterion of the Charter is materially unmet by the un-remediated findings AND
  • IF the fix surface fits in one cohesive PR (~ < 25 files, no architectural reopen)
  • THEN apply Batch 7.4 amendment instead of opening a new Charter

Telemetry slot suggestion: add to charter_telemetry:

post_close_amendment:
  applied: true | false
  trigger: "external_audit" | "production_incident" | "deferred_implementation"
  ailog_id: AILOG-YYYY-MM-DD-NNN (the amendment AILOG)
  findings_closed: <count>
  files_modified: <count>
  effort_hours: <float>

so the framework can correlate amendment frequency against original Charter quality + audit-cycle yield.

Cross-pattern observation — they compose

A charter that received the pre-declare refresh (Pattern 1) is more likely to avoid the post-close amendment (Pattern 2), because the refresh absorbs the pre-execution risk that the audit would have surfaced post-close.

But CHARTER-18 needed both — the refresh handled the spec-level drift (architectural assumptions, table naming, framework version), and the amendment handled the runtime-level drift (DI wiring that the spec didn't reach into). The two patterns are complementary, not substitutable. StrayMark's framework should encourage Pattern 1 at the chain level (module-wide retrospective) and tolerate Pattern 2 at the cycle level (post-audit per-charter remediation).

Adopter contribution

Sentinel's adopter (StrangeDaysTech/sentinel, operator José Villaseñor Montfort) is contributing this RFC as feedback to StrayMark v1 governance + telemetry schema iteration. The patterns are already live in Sentinel's .straymark/ tree; this issue codifies them for upstream consumption.

References:

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