Skip to content

chore(conventions): route unstamped upstream-fact carriers into upstream-drift, and record the gate deferral - #2318

Merged
kyle-sexton merged 1 commit into
mainfrom
chore/2273-upstream-drift-adoption
Aug 12, 2026
Merged

chore(conventions): route unstamped upstream-fact carriers into upstream-drift, and record the gate deferral#2318
kyle-sexton merged 1 commit into
mainfrom
chore/2273-upstream-drift-adoption

Conversation

@kyle-sexton

Copy link
Copy Markdown
Contributor

Summary

Plugins restate upstream harness facts in bare prose — no source URL, no as-of date, no recheck
trigger — so when the upstream behaviour changes, nothing makes the stale claim surface.
docs/conventions/upstream-drift/ already owns exactly this concern and already says the name
binds on touch. The gap was adoption, not design, so nothing new is invented here.

Docs-only. No plugin, script, hook, or CI behaviour changes.

1. The Adopters table now says how a post-1.0.0 row gets there — and what disqualifies one

The preamble read "Migrated at this contract's 1.0.0", which any appended row would have quietly
falsified. It now separates the eight migrated rows from later on-touch adoptions and requires each
later row to name the release that added it.

It also writes down the bar the third column already implied but never stated: a surface is tabled
only once it actually conforms.
That column is a promise to a reader about what they can rely on,
so a carrier known to be unstamped belongs in a tracked issue, never in a row — tabling it would
assert the very thing the reader would then not get, which is this batch's own recurring defect
class pointed at the convention meant to prevent it.

2. The fleet sweep — 12 carriers, filed rather than tabled

A sweep of plugins/** for surfaces stating an upstream harness behaviour with no source, date, or
trigger found 12 carriers across 11 plugins, filed at #2297 with quoted lines and a per-plugin
tally. The issue also records the files checked and found conforming (so the sweep is not
one-sided) and states its own limitation plainly: the citation gate was file-level, so any file
containing a docs URL anywhere was dropped from the carrier pass, which structurally
under-represents mixed files — roughly 300 files were dropped that way. It is a lower bound, not a
census
, and it says so.

None is tabled here, which is change 1 applied to its own findings.

3. architecture is deliberately NOT added to the table

Row 1 of #2273 asks for exactly that row. It is not being added, and the reason is the point:

#2286 (merged while this was in flight) removed the false substitution claim, closing #2207. But the
surviving bullet on main still reads:

The durable candidate artifact is a per-project memory-tier file, never ${CLAUDE_PLUGIN_DATA}.
Even resolved it points at a plugin-global dir that collides candidates across projects.

"Even resolved it points at a plugin-global dir" is an upstream-owned specific —
plugins-reference §Environment variables → §Persistent data directory, which resolves the token to
~/.claude/plugins/data/{id}/. It carries no URL, no date, and no trigger. So A-F4 (the unstamped-
prose row of #2207) is untouched and architecture is still a carrier. Per change 1 it is recorded
in #2297, not tabled.

4. Enforcement decided: deferred, as a recorded decision with its own trigger

Row 3 asks whether this repo's *-gate CI pattern should enforce adoption. No gate is built,
and the reason is a finding rather than a preference:

The candidate check §Enforceability already namesflag any Verified <date> line or row whose
surface states no trigger
would not have caught #2207, the case that prompted the question.
That surface carried no stamp at all, so a stamp-anchored grep had nothing to match on. The named
check is shaped for a half-conforming record; the failure that actually ships is the
zero-part one. The existing named-not-built check and its own build trigger are left unchanged.

The zero-part shape has no deterministic check available: deciding whether a sentence restates an
upstream-owned specific — as against an in-repo fact or ordinary prose — is a judgment about
meaning, reasoning-only under the tiers doc. A harness-vocabulary grep (PostToolUse,
${CLAUDE_*}, settings.json) fires on every correct citation and in-repo mention alike, and a gate
whose false-positive rate forces routine suppression trains authors to bypass it — worse than no
gate, because it converts a real signal into noise with an approved silencer.

Recorded as the doc's own second record kind with a basis and an event trigger — a third
unstamped carrier reaching main, or a demonstrated detector needing no suppression list — so the
deferral expires on evidence rather than on a date.

Disposition of all three rows of #2273

Row Disposition
Add architecture to Adopters Not added, deliberately#2286 left an unstamped upstream specific in the same bullet, so the plugin does not conform. Recorded in #2297 instead, per the tabling rule this PR adds.
Sweep fleet-wide, add each carrier Swept. 12 carriers / 11 plugins filed at #2297 with evidence, conforming counter-examples, and the sweep's stated limits. Not tabled, same rule.
Decide enforce vs document Decided: deferred, recorded with basis and trigger, on the finding that the named candidate check would have missed the motivating case.

upstream-drift 1.3.0 → 1.4.0 (minor: additive guidance, per the contract's own versioning
rule — no required part, canonical name, or enforceability verdict changed).

Test plan

$ npx markdownlint-cli2 docs/conventions/upstream-drift/README.md \
    docs/conventions/upstream-drift/CHANGELOG.md
markdownlint-cli2 v0.23.2 (markdownlint v0.41.1)
Linting: 2 files
Summary: 0 issues in 0 files

$ bash scripts/check-changelog-parity.sh --check-order
All 75 changelog(s) read newest-first with no duplicate versions.

$ bash scripts/check-changelog-parity.sh --check-bump $(git merge-base origin/main HEAD)
Every plugin whose version changed vs c722271c723e1b0dd1196697a0100e961b06dd94 has a '## [<version>]' CHANGELOG.md entry.

--check-order and --check-bump both read docs/conventions/*/CHANGELOG.md, so the convention
changelog's ## 1.4.0 — 2026-08-12 heading form and its ordering are exercised by the run above,
not assumed.

The claim in §3 is quoted from main rather than recalled:

$ git show origin/main:plugins/architecture/skills/improve/SKILL.md | sed -n '71p'
- **The durable candidate artifact is a per-project memory-tier file, never `${CLAUDE_PLUGIN_DATA}`.** Even resolved it points at a plugin-global dir that collides candidates across projects. ...

The upstream basis for calling that an upstream-owned specific was re-fetched at rung 1 during this
work — https://code.claude.com/docs/en/plugins-reference.md, 2026-08-12 UTC, 200,
text/markdown, 95,338 bytes / 1,314 lines, first heading # Plugins reference, slug confirmed
canonical against llms.txt, SHA-256
f6627de35a3f285d18cf22494843bb328d65e3b867fbc1856865caa47ea3ea64 — line 709: "The
${CLAUDE_PLUGIN_DATA} directory resolves to ~/.claude/plugins/data/{id}/ …".

Related

…eam-drift, and record the gate deferral

Plugins restate upstream harness facts in bare prose — no source, no as-of
date, no recheck trigger — so when the upstream behaviour changes nothing makes
the stale claim surface. The convention already owned verification stamps,
recheck triggers, and the rule that the name binds on touch; the gap was
adoption, not design.

Three changes, all additive guidance (1.3.0 -> 1.4.0):

1. The Adopters preamble read "Migrated at this contract's 1.0.0", which any
   appended row would have quietly falsified. It now separates the migrated
   rows from later on-touch adoptions, requires each later row to name the
   release that added it, and writes down the bar the third column already
   implied: a surface is tabled only once it CONFORMS, so a carrier known to be
   unstamped is a tracked issue rather than a row. That column is a promise to
   a reader.

2. A fleet sweep of plugins/** found 12 carriers across 11 plugins, filed at
   #2297 with quoted lines, the files checked and found conforming, and the
   sweep's own limitation stated (a file-level citation gate under-represents
   mixed files, so the list is a lower bound). None is tabled here, per the
   rule above.

3. A CI gate for adoption is deferred, recorded as a decision with a basis and
   an event trigger. The load-bearing finding: the check the doc already names
   — flag a `Verified <date>` with no trigger — would NOT have caught #2207,
   the case that prompted the question, because that surface carried no stamp
   at all for a stamp-anchored grep to match. The named check is shaped for a
   half-conforming record; the failure that ships is the zero-part one.
   Separating an upstream restatement from an in-repo fact is a judgment about
   meaning, reasoning-only under the tiers doc, and a harness-vocabulary grep
   would fire on every correct citation too.

architecture is deliberately NOT added to the table: #2286 removed the false
substitution claim but left an unstamped upstream specific in the same bullet
("even resolved it points at a plugin-global dir"), so the plugin does not yet
conform. It is recorded in #2297 instead — which is exactly what change 1 says
to do.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@cursor

cursor Bot commented Aug 12, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 78527a17b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docs/conventions/upstream-drift/README.md
Comment thread docs/conventions/upstream-drift/README.md
@claude

claude Bot commented Aug 12, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 5m 13s —— View job


Review complete

  • Gather context (CLAUDE.md, REVIEW.md, diff)
  • Understand the request (code review lane)
  • Review docs/conventions/upstream-drift/README.md changes
  • Review docs/conventions/upstream-drift/CHANGELOG.md changes
  • Check version bump / changelog parity claims
  • Post findings

Summary

Docs-only change to docs/conventions/upstream-drift/ (README.md +43/-1, CHANGELOG.md +34). I diffed c722271...78527a1 (merge base → head) and independently checked the load-bearing factual claims rather than taking the PR body at its word. Everything I could check against repo state held up:

  • §3's core claim — that plugins/architecture/skills/improve/SKILL.md still carries an unstamped upstream specific ("Even resolved it points at a plugin-global dir...") on main — I confirmed myself via git show origin/main:plugins/architecture/skills/improve/SKILL.md, independent of the PR's own quoted verification.
  • The two "already on the record" incidents cited in the new Recorded-decision §Recheck trigger (README.md, added ~line 305): plugin-quality's ${CLAUDE_PLUGIN_DATA} claim was in fact corrected in its [0.4.0] entry (plugins/plugin-quality/CHANGELOG.md:146), and architecture's false substitution claim was in fact removed in [0.5.1] (plugins/architecture/CHANGELOG.md:6) closing fix(architecture): improve's Gotcha asserts ${CLAUDE_PLUGIN_DATA} does not substitute in skill content — plugins-reference says it does #2207 — matches git log's a12b340 fix(architecture): remove false CLAUDE_PLUGIN_DATA substitution claim from improve Gotcha (#2286).
  • Changelog mechanics: the new ## 1.4.0 — 2026-08-12 heading uses the same no-brackets em-dash form as the 5 preceding entries (1.3.0→1.0.0), correctly newest-first, no duplicate versions, no stale version string left elsewhere in the tree (grep for upstream-drift.*1\.[0-9]\.[0-9] across the repo found nothing outside this CHANGELOG). MD013 (line length) is disabled repo-wide in .markdownlint-cli2.jsonc:32, so the long prose lines aren't a lint risk.

One finding, not tied to a changed line so it goes here rather than inline:

The Test Plan states "--check-order and --check-bump both read docs/conventions/*/CHANGELOG.md, so the convention changelog's ## 1.4.0 — 2026-08-12 heading form and its ordering are exercised by the run above, not assumed." I read scripts/check-changelog-parity.sh rather than running it (script execution wasn't available in this sandbox — see note below), and that claim is only half true:

  • --check-order does read docs/conventions/*/CHANGELOG.md — confirmed at scripts/check-changelog-parity.sh#L211.
  • --check-bump never touches it. Its manifests array is scoped to plugins/*/.claude-plugin/plugin.json only (L90, looped at L382), and docs/conventions/ has no plugin.json manifest to key off of. So the --check-bump run in the Test Plan output ("Every plugin whose version changed...") iterated zero conventions and asserted nothing about this file's heading form.

This doesn't affect the shipped doc content — --check-order's tolerant regex (^\[?[0-9]+\.[0-9]+...\]?) would accept the heading either way, and the bracket-less form matches this file's own established convention. It's a low-severity inaccuracy, but worth flagging given this PR's entire subject is making sure claims are backed by what was actually checked rather than assumed.

Not independently verified (no network/script execution available in this sandbox — flagging per review instructions rather than treating the PR's own Test Plan output as my verification): the markdownlint-cli2 run, the --check-order/--check-bump command output shown in the Test Plan, and the re-fetch/SHA-256 of plugins-reference.md cited for the §3 basis. These are author-claimed; I did not run npx, gh, or the shell scripts myself (command approval was unavailable for anything beyond git/grep/sed/sandboxed reads in this session) — if verifying those directly matters, this lane's --allowedTools would need Bash(npx:*)/Bash(scripts/*.sh:*) widened.

No correctness issues found in the actual diff content — the Adopters-table preamble, the new tabling rule, and the Recorded-decision section are internally consistent with each other and with the doc's pre-existing four-part-record / recorded-decision definitions (§Required parts, README.md:56-59).

@github-actions

Copy link
Copy Markdown

Claude has reviewed this PR 1 time. The lane skips further automatic reviews after 5; deleting this comment resets the count.

@kyle-sexton
kyle-sexton merged commit b3fe830 into main Aug 12, 2026
35 checks passed
@kyle-sexton
kyle-sexton deleted the chore/2273-upstream-drift-adoption branch August 12, 2026 03:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore(conventions): plugins restating upstream harness facts are not adopters of upstream-drift, so stale claims never surface

1 participant