Skip to content

fix(skills): the drift-gate section names the gate's script, not the instrument it stopped using - #57

Merged
github-actions[bot] merged 1 commit into
mainfrom
feat/drift-gate-prose
Oct 29, 2026
Merged

github-actions[bot] merged 1 commit into
mainfrom
feat/drift-gate-prose

Conversation

@aitrawog

Copy link
Copy Markdown
Contributor

Summary

Head ce4016175a49a0619bf5a314fef7d839b6f025b4 — 1 file changed, 20 insertions(+), 5 deletions(-).

This repo's marketplace skill — the SOURCE the corpus refs list pins DIRECTLY, and the file the corpus copy, the docs mirror and the docs recipe page are all projections of — described the marketplace repo's drift gate as

including UNTRACKED regenerated files (git status --porcelain, not just git diff --exit-code)

That was the gate's OLD instrument, and the attribution was wrong. The gate is the marketplace repo's scripts/drift-gate.sh — ONE implementation, run by deploy.yml on every PR and by the daily refresh on the tree its own PR commits — and it asks "did the tree move since the index was set": the working tree against the index (git diff --quiet), and the files in NO index at all (git ls-files --others --exclude-standard).

And the old instrument was wrong FOR THAT TREE SHAPE. git status --porcelain reports the INDEX column as well as the worktree column, and the refresh stages its regenerated corpus on purpose — so the index differs from HEAD by design, and a gate built on that comparison fails on the refresh job's tree shape, every time it runs there. Measured 2026-10-29 on the refresh's own tree with the pinned generator (charly 2026.296.2116): a check built on it aborted with DRIFT on a tree that had not moved at all, listing the whole staged corpus.

That is a property of the INSTRUMENT on that tree shape, and not a claim about any run: the refresh ran NO gate before 2026-10-29 (it pasted deploy.yml's gate SOURCE into its body), so no refresh ever aborted on it. The sentence that turned the measurement into a false history is corrected in opencharly/marketplace#596 (squash d074818, tag v2026.302.0520), whose body is the erratum entry; the changelog entry it corrects is immutable, because it is the projection of a body at merge time.

So this body names the gate's script and its two callers, names the two halves the gate actually checks, and says plainly why the gate does NOT use git status --porcelain. The teaching the old parenthetical carried — that the gate catches UNTRACKED regenerated files and not only a diff — is unchanged and still true; only the command it named is not.

The marketplace repo's half of the fix — the shared gate script, its control, and the refresh job that RUNS the gate on the tree its PR commits — landed as opencharly/marketplace#594, and it lands FIRST: per R4 the code is fixed before the prose that describes it, and this file is the source every projection of the skill is generated from. The RCA that corrects the false counterfactual in that repo's published RCA landed as opencharly/marketplace#596, and this repo's half of the same finding is #56.

Closes #56

How tested

The change class is documentation-only — one .md file, no code, no pin, no behavioral config — so the gate is this repo's non-runtime standards, run on the committed tree below (the tree this PR pushes, not a working copy of it):

  • the plugin module's go build ./... (compile),
  • the plugin module's go test -count=1 ./... (the generator's Go tests, re-run uncached),
  • charly box validate at the repo root (the structural check: the candy, the plugin: block, the CUE schema).

No disposable bed is claimed: this repo's check-marketplace bed is a RUNTIME witness, and a runtime tier is forbidden for prose-only work. The toolchain is a worktree-local build of the pinned tag v2026.296.2116 — charly version prints 2026.296.2116 — never a shared install.

$ git rev-parse HEAD
ce4016175a49a0619bf5a314fef7d839b6f025b4
$ git status --porcelain -uall
(no output; the tree is clean)
$ charly version
2026.296.2116
$ go build ./...
(no output)
RC_build=0
$ go test -count=1 ./...
ok  	github.com/opencharly/plugin-marketplace/candy/plugin-marketplace	2.858s
?   	github.com/opencharly/plugin-marketplace/candy/plugin-marketplace/cmd/serve	[no test files]
RC_test=0
$ charly box validate
charly box validate: OK — checked 1 candy, 1 box, 1 deploy, 7 distros, 7 builders; 0 warnings, 0 errors
RC_validate=0

A FOURTH check in this repository is RED, and it is not this change's gate. charly task marketplace:pin compares this repository's declared var: MARKETPLACE_REF with the
marketplace repo's CURRENT main head, and it is red: the corpus repo's main advanced —
opencharly/marketplace#594 and opencharly/marketplace#596 both landed in it — while this
repository's declared pin stayed where it was. That is a PIN-CURRENCY question, independent of
this prose change, which touches no pin and no generator; it is carried, with its RCA and the
bump's producer-first order, by #50, whose bump lands after the
corpus refresh that republishes this fix. The measured state is pasted here rather than left to
be discovered:

$ /home/atrawog/Sync/Atrapub/a/claude/opencharly/.worktrees/mkt-refreshbody/charly-pin/bin/charly -C . task marketplace:pin
task marketplace:pin: 3 step(s), 2 failed
  [fail] check — the declared MARKETPLACE_REF var equals the marketplace repo's current main head
      exit=1, want 0 (stderr: )
  [fail] check — the duplicated literal in the check step equals the same head
      exit=1, want 0 (stderr: )
  [pass] check — the candy's file carries exactly one 40-hex contains anchor
      exit=0
charly: error: command "task": task "marketplace:pin" failed (2 step(s) failed)

The R5 sweep for this change is claim-keyed. The retired parenthetical is gone from this repo: the grep for it returns nothing, and the exit status is the evidence.

$ git grep -n 'including UNTRACKED regenerated files' -- .
(no output)
exit: 1

The instrument itself is still named in three kinds of place, and every one of them is a DIFFERENT thing from the gate's old description, so each is listed here rather than swept away:

$ git grep -n 'git status --porcelain' -- .
.agents/skills/marketplace/SKILL.md:70:deliberately does NOT use `git status --porcelain`: that command reports the index column
CHANGELOG/2026.241.0124.md:68:$ git status --porcelain      # empty
CHANGELOG/2026.288.2256.md:28:$ git status --porcelain          # prints nothing: the gate ran on the committed tree
CHANGELOG/2026.288.2256.md:62:**R7 — the new test fails without the change.** Reverting the one line in the working tree and re-running it (the tree is restored afterwards; `git status --porcelain` is empty again):
CHANGELOG/2026.288.2256.md:84:$ git status --porcelain                                         # prints nothing: back at this head
CHANGELOG/2026.288.2256.md:217:- **R6** — work happens in a session worktree branched off `origin/main` (`bf8d87f`, whose parent is `ccc9074`); `git status --porcelain` is empty before and after every run pasted above; the marketplace checkout is a foreign tree that this change only reads, and it is verified unmodified on both sides of the drift run; no force-push, no history rewrite, no hook bypass, and this head has never been pushed, so nothing was amended after a push.
CHANGELOG/2026.288.2313.md:33:$ git status --porcelain          # prints nothing: the gate ran on the committed tree
CHANGELOG/2026.288.2313.md:68:**R7 — the new test fails without the change.** One line reverted in the working tree (restored afterwards; `git status --porcelain` is empty again), and the test prints the two index files it compared:
CHANGELOG/2026.288.2313.md:123:- **R6** — work happens in a session worktree; `git status --porcelain` is empty before and after every run pasted above; the branch was rebased onto the current `origin/main` **before its first push** (it has never been pushed), so no pushed history was rewritten; the marketplace checkout is read only, and it is verified unmodified on both sides of the drift run; no force-push, no hook bypass.
CHANGELOG/2026.288.2341.md:7:**Which side holds the intent.** *The issue's own scope offers "declare the recipes, or delete the seven references", and R4 settles it: fix the code before the prose, having established which holds the intent.* **`AGENTS.md` names the first as this repository's gate for a generator change** — *"the arm that adds the `git status --porcelain` half to the fail-closed `drift` no-op"* — **and a gate that does not exist is worse than no gate, because the documents teach a reader to trust it.**
CHANGELOG/2026.288.2341.md:33:| **`skills:drift`** | *the fail-closed `charly marketplace drift` no-op **plus** the `git status --porcelain` half, in ONE recipe* — *because the command's own doc says it "never consults git… and says nothing about whether the tree is committed".* **`--out` is a declared `param`.** |
CHANGELOG/2026.289.0219.md:96:$ git status --porcelain | wc -l
CHANGELOG/2026.297.1411.md:150:$ git status --porcelain > /tmp/pm-42-restore.log 2>&1
CHANGELOG/2026.297.1411.md:229:$ git status --porcelain
CHANGELOG/2026.297.1419.md:114:$ git status --porcelain > /tmp/pm-42b-restore.log
candy/plugin-marketplace/generate.go:20:// at all. `task skills:drift` is what adds the `git status --porcelain` half.
exit: 0
  • SKILL.md:70 — the sentence that says the gate does NOT use it.
  • candy/plugin-marketplace/generate.go:20 — the plugin's OWN task skills:drift recipe, a different check that asks whether the tree is COMMITTED (the generator's drift verb never consults git at all). Its use of the command is that recipe's own business, and naming it there is not the drift-gate description.
  • the CHANGELOG/ hits — this repo's own history: merged entries are the projection of past PR bodies and are not edited.

And the false counterfactual is gone from this repo's prose:

$ git grep -n -E 'every refresh run|no PR would ever|would abort every|the corpus would freeze' -- .
(no output)
exit: 1

Rulebook compliance

  • R0: The skills this area's dispatcher rows select were loaded before the first tool action: /charly-internals:marketplace (the corpus contract and the drift gate this body describes), /charly-internals:plugin (this repo's plugin surface), /charly-internals:root-cause-analyzer (the RCA of the instrument defect), /charly-internals:strict-policy (the finding's classification), and /charly-internals:git-workflow (the PR gate).
  • R1: The first failure — the gate aborting with DRIFT on a tree that had not moved — got a fresh RCA before any remediation. Its mechanism (the INDEX column of git status --porcelain), its missed control (nothing tied the published prose to the gate's instrument: the gate lived as an inline step in EACH of the two workflows, so no single place named it, and a prose edit needs no regeneration to be wrong), its blast radius (every reader of the published skill is taught an instrument that aborts on the refresh's own shape) and its root fix (the instrument, plus the control that catches it) are in the commit message and in fix(refresh): the body's drift gate is RUN in the job, and there is ONE gate marketplace#594. The false HISTORY that followed that measurement is corrected in fix(corrective): the drift RCA stated a false history, and the claim about the second caller was unchecked marketplace#596 (squash d074818, tag v2026.302.0520), whose body is the erratum entry. This repo's half of the same finding is fixed in this change, and the marketplace skill names the drift gate's old instrument (git status --porcelain) — the gate is now scripts/drift-gate.sh and does NOT compare the index with HEAD #56 carries the RCA. The one finding this change's own pass produced — the stale MARKETPLACE_REF, measured and pasted above — is non-blocking for a prose change that touches no pin and no generator, so it is carried by the declared MARKETPLACE_REF can go stale again, and nothing would see it #50 rather than fixed here.
  • R2: The body names ONE gate and its TWO callers — the marketplace repo's scripts/drift-gate.sh, run by deploy.yml and by the refresh. It does not describe a second implementation of the check, and it does not confuse the plugin's own task skills:drift recipe with the CI gate.
  • R3: No sleep, no retry, no suppression, no workaround: the body names the gate that runs and the instrument it uses.
  • R4: The product is fixed before the prose. The gate is the product; it lands in fix(refresh): the body's drift gate is RUN in the job, and there is ONE gate marketplace#594 FIRST, and this prose lands after it.
  • R5: The retired instrument is deleted from the gate's own description in this file, proven by the claim-keyed grep above; the surviving mentions are listed above, each with what it actually is.
  • R6: git status --porcelain -uall was empty before every action in this worktree; no force-push, no history rewrite, no hook bypass, no direct push to main. This head has never been pushed, so the one commit was reworded before its first push.
  • R7: The changed path is a prose file; there is no code path to execute live, so the coverage claim is the claim-keyed greps above, which return nothing for the retired phrasing and for the false counterfactual, and return exactly the mentions that are listed and explained. The three non-runtime standards ran live on the committed tree, with the test run forced uncached (-count=1).
  • R8: Nothing is mocked. The structural check ran against the real manifest with the real pinned charly; no live service is involved in a prose-only change.
  • R9: The toolchain is a worktree-local build of the pinned tag v2026.296.2116 (charly version → 2026.296.2116), not a shared install.
  • R10: The change-class gate for a documentation-only diff is this repo's non-runtime standards, and it ran on the committed tree below with zero warnings (the structural check reports 0 warnings, 0 errors). This repo builds and deploys the corpus box in its check-marketplace bed; that bed is a runtime witness and is deliberately not claimed for prose-only work.

Change classification

Class: a documentation-only change — one skill body, .agents/skills/marketplace/SKILL.md, the source of the published marketplace skill. No Go code, no schema, no candy, no box, no pin: nothing here ships inside an image.

Gate: this repo's non-runtime standards — go build ./..., go test -count=1 ./... and charly box validate — run on the committed tree, as pasted above. The consumer of this file is the corpus regeneration in opencharly/marketplace, whose own gate is opencharly/marketplace#594.

Tier: documentation reviewed — the whole diff is one .md file, and every non-runtime standard passed on the committed tree with zero warnings.

Filed issues

  • the declared MARKETPLACE_REF can go stale again, and nothing would see it #50 — the declared var: MARKETPLACE_REF is behind the marketplace repo's served head: charly task marketplace:pin is RED (two of its three steps fail, measured and pasted under How tested), because the corpus repo's main advanced while this repository's pin did not. It is non-blocking for this change: the finding is a pin-currency question, this diff touches one .md file and no pin and no generator, and the fix is separable from it. The issue carries the RCA and the producer-first order; its bump lands after the corpus refresh that republishes this fix, because the pin names the corpus repo's CURRENT main head and that head moves with every merge there. This session's evidence was added to that issue rather than filed twice.

This change also closes #56, which this session filed for the stale instrument and this change discharges: the body now names the gate's script, its two callers, the two halves the gate checks, and why the gate does not use git status --porcelain. The marketplace repo's half of the same finding landed in opencharly/marketplace#594, and the RCA that corrected its false history landed in opencharly/marketplace#596.

Agent: charly · session ses_8efb271d-f2c0-47d3-85e3-2a1e00b11ae0

Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (documentation reviewed)

…instrument it stopped using

The published skill taught the gate's OLD instrument — "including UNTRACKED
regenerated files (`git status --porcelain`, not just `git diff --exit-code`)" — and
that command reports the INDEX column as well as the worktree column. The refresh
job stages the regenerated corpus on purpose, so the index differs from HEAD by
design and the condition is true BY CONSTRUCTION on that tree shape: measured
2026-10-29 on the refresh's own tree with the pinned generator (charly
2026.296.2116), a check built on it aborted with DRIFT on a tree that had not moved
at all, listing the whole staged corpus. A gate built on it fails on the refresh
job's tree shape, every time it runs there.

That is a property of the INSTRUMENT on that tree shape, not a claim about any
run: the refresh ran NO gate before 2026-10-29 (it pasted deploy.yml's gate
SOURCE into its body), so no refresh ever aborted on it. The sentence that got
this wrong is corrected in opencharly/marketplace#596, whose body is the erratum
entry; the changelog entry it corrects is immutable, because it is the projection
of a body at merge time.

The gate is now scripts/drift-gate.sh — ONE implementation, run by deploy.yml on
every PR and by the refresh job on the tree its PR commits — and it asks "did the
tree move since the index was set": the working tree against the index
(git diff --quiet) and the files in NO index at all
(git ls-files --others --exclude-standard), the half git diff alone misses.

So this file names the gate's script and its two callers, names the two halves the
gate actually checks, and says why it does NOT use git status --porcelain. The
teaching the old parenthetical carried — that the gate catches UNTRACKED
regenerated files and not only a diff — is unchanged and still true; only the
command it named is not.

The marketplace repo's half of the fix — the shared gate script, its control, and
the refresh job that RUNS it on the tree its PR commits — is opencharly/marketplace#594,
and it lands FIRST: per R4 the code is fixed before the prose that describes it,
and this file is the source every projection of the skill is generated from. The
RCA that corrects the false counterfactual in that repo's published RCA is
opencharly/marketplace#596, and this repo's half of the same finding is
#56.

Closes #56

Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (documentation reviewed)
@github-actions

Copy link
Copy Markdown

Review — PASS

What the diff does

The single changed file, .agents/skills/marketplace/SKILL.md, rewrites the "drift gate" section: the header changes from deploy.yml is the SOLE OWNER of generation to naming the marketplace repo's scripts/drift-gate.sh with "ONE implementation, TWO callers"; the old parenthetical naming git status --porcelain is removed and replaced with an accurate description of the gate's two halves (git diff --quiet for worktree-vs-index, git ls-files --others --exclude-standard for untracked files) plus an explicit rationale for why git status --porcelain is not used. The teaching the old parenthetical carried (the gate catches untracked regenerated files, not just a diff) is preserved in the new prose.

Correctness & honesty

  • Metadata matches. Body claims "1 file changed, 20 insertions(+), 5 deletions(-)"; the diff header reports modified, +20/-5 and the changed-files section lists exactly one file. Consistent.
  • Grep evidence reproduces. The body's R5 sweep cites .agents/skills/marketplace/SKILL.md:70:deliberately does NOT use git status --porcelain…. Counting the new hunk from +56, that sentence lands on exactly line 70. The retired phrase including UNTRACKED regenerated files is the text the diff deletes, so the grep-returns-nothing claim is reproduced by the diff.
  • Technical soundness. git status --porcelain reports the index column; a refresh that stages its regenerated corpus makes the index differ from HEAD, so a gate built on the raw status output would trip on a tree that has not moved relative to its index. git diff --quiet (worktree↔index) and git ls-files --others (untracked) are the right pair for "did the tree move since the index was set." The prose is accurate on this point.
  • Date consistency. The measurement is dated 2026-10-29, which is day 302 of 2026; the cited erratum tag is v2026.302.0520 (day 302). Internally consistent.
  • Scope. Documentation-only; no code, schema, pin, or generator touched, which is exactly what the body claims.

Tests / gate

The body is explicit that this is a documentation-only class and claims only the non-runtime standards (go build, go test -count=1, charly box validate) with pasted output. That is a defensible gate for a prose change and it is disclosed honestly. The one RED check, charly task marketplace:pin, is measured, pasted, and correctly reasoned as independent of this diff (this PR changes no MARKETPLACE_REF and no generator), with the fix carried by #50. Disclosure of a red, unrelated check with a sound non-blocking rationale is the honest handling, not a suppression.

Comments

There are 0 comments, so there is no maintainer sign-off to weigh. The change therefore stands on its own evidence — the diff is internally consistent, the grep evidence reproduces against the actual hunk, and the technical description is sound. It does not require an external sign-off to be correct.

Residual notes (non-blocking)

The cross-repo claims (opencharly/marketplace#594 / #596, the erratum tag, the measurement) cannot be independently verified from this PR's diff alone; they are cited, dated, and internally coherent, and the local prose fix is correct regardless. This is normal for a projection-source fix and does not warrant a block.

Verdict: PASS


ollama-cloud/deepseek-v4.1-flash — action-review.

View action run

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

Labels

None yet

Projects

None yet

1 participant