Skip to content

docs(agents): record how the Actions workflow registry is keyed, and the paginated-listing rule - #6183

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-6069-workflow-registry-note
Aug 25, 2026
Merged

docs(agents): record how the Actions workflow registry is keyed, and the paginated-listing rule#6183
os-zhuang merged 1 commit into
mainfrom
claude/issue-6069-workflow-registry-note

Conversation

@yinlianghui-tw

Copy link
Copy Markdown
Collaborator

Fixes #6069

GOVERNED SURFACE — this PR must stay draft and must NOT be armed. AGENTS.md is the first item named in AGENTS.md §受管面 (maintainer ruling 2026-08-18), so the hard rule applies: no gh pr ready, no merge queue, no auto-merge. A human merges it, and that merge is the review record. Flagging it here because the criterion is the file list, and this PR's file list is exactly one governed file.

Round 2 of #6069, implementing the PM ruling of option A on both questions: a hazard note, nothing else. ⛔ No gate, no deregistration, no workflow edits. One file, +59 / -0.

What the note states

Placed in §9 Operational Rules, immediately before §受管面.

  1. The registry is keyed by the workflow's FIRST RUN on any ref. The default branch is irrelevant, and the entry then persists. So a workflow file that ran once on any PR branch is registered forever, even if it never reached main and the branch was abandoned.
  2. state: "active" means "not disabled". It is not a claim about main. That is the whole false friend.
  3. The general paginated-listing rule. list_workflows ignores per_page and returns a fixed 30 entries while truthfully reporting total_count in the same JSON body — so compare total_count against the returned array length before believing any absence. Written as a general rule for paginated listings rather than a workflow-specific one, per the ruling on Q2: the failure is generic and this is the cheapest place to stop the class.
  4. The sound way to answer "does X run here?" is git cat-file -e against origin/main with a positive control (ci.yml) — one command, no CI, no heuristic. The note says explicitly that the positive control is not optional, because a mistyped path and a real absence produce the same exit code.

It also records why the cross-check gate was rejected, so the next reader does not re-propose it.

⚠️ What the note says it does NOT know

A hazard note that overstates its own coverage is the defect it exists to prevent, so two boundaries are stated in the note itself:

  • The delete-from-default-branch behaviour is UNKNOWN and UNTESTED here. git log origin/main --diff-filter=D --name-only over .github/workflows/ returns empty, and the set of paths ever present in main's history is identical to today's set. That transition has never been exercised in this repository, so "the entry then persists" is scoped to files that never reached main — they had no default-branch deletion event available to fire.
  • The API-vs-human-tab question is OPEN, not answered. Both github.com/objectstack-ai/objectui/actions and api.github.com return 403 for these sessions, so the MCP tool is the only reachable registry view and the two were never compared.

⭐ Every figure re-measured at claim, and one of them moved

The dispatch required re-taking the 7 / 25 / 36 figures rather than restating them. Re-measured, and the tree had in fact moved — PR #6159 merged at 22:58:38Z, between the first fetch and the measurement, which changed one of the seven:

reading round 1 re-measured at c677fe3b8
registry total_count 36 36
entries returned on page 1 30 30 (per_page: 100 ignored)
entries on page 2 6 6 — union 30 + 6 = 36
registered file-path entries 32 32 (plus 4 dynamic/*)
workflow files on main 25 26
Direction A — registered, not on main 7 6
Direction B — on main, not registered EMPTY EMPTY
ever-present path set vs today 25 = 25 26 = 26

The entry that left Direction A is pre-install-import-graph.yml — the healthy in-flight counter-example the ruling turned on. It was registered at 21:41Z, was Direction A's seventh member when this round began, and graduated to main mid-measurement when #6159 landed. ⭐ That is the gate rejection re-confirmed rather than quoted: a cross-check gate open during those 77 minutes would have reddened a completely healthy PR. The note carries it as the worked example.

Merge state was read from the PR state directly (merged: true, merged_at), never git branch -r --contains — merges here are squashes.

Verification, each quoting its own verdict line

Exit codes captured before any pipe (redirect first, then tail). Union re-run at the final commit 0c36cd3c4, working tree clean.

node scripts/check-control-bytes.mjs        exit=0
  ✅  check-control-bytes: OK (scanned 5106 tracked text file(s); skipped 85 binary).
node scripts/check-doc-links.mjs            exit=0
  Links are valid across 15 scan roots.
node scripts/check-changeset-presence.mjs   exit=0
  ✅  No source of a released package changed in this range, so no changeset is owed.
npx vitest run scripts/__tests__/ --maxWorkers=2   exit=0
  Test Files  71 passed (71) · Tests  1933 passed (1933)

Heavy steps ran through the container's shared verify lock; its own verdict lines read command-exit 0 · held the lock 43s for the suite and 6s for the install.

Gate scope, derived from each gate's own configuration rather than assumed:

  • check:doc-fencesnot implicated. check-doc-fence-languages.mjs scans content/docs plus packages/*/README.md (DOCS_ROOT/DOC_EXTENSIONS in its source). A root AGENTS.md is outside its population, so the added bash fence is not in its scope.
  • lint:rootnot implicated, measured not assumed. eslint --format json AGENTS.md returns one file object carrying "File ignored because no matching configuration was supplied." — the file is not in eslint's population at all, read from eslint's own config resolution.
  • Root vitest only, per objectui#3378 — never package-scoped. Scoped to scripts/__tests__/, which is the honest superset: check-doc-links.test.ts is the one suite that reads AGENTS.md from the real tree (it asserts the file is a SCAN_ROOTS row that opens exactly one file, and walks every markdown link in it). The whole directory was run rather than that one file.

No changeset is owed and none was added — the presence gate's own verdict line says so above, over this range. objectui has no skip-changeset label mechanism, so nothing was labelled; the declaration here is the gate verdict itself.

Control bytes: grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' AGENTS.md returns no matches (exit 1), alongside the green gate above.

Link discipline: the note adds no markdown links, and AGENTS.md has none at all — consistent with check-doc-links.mjs's own header note, and the reason its disk rule row still passes unchanged.

Not done, and not owed by this seat

CI convergence. The PM verifies the real gate jobs. And per the governed-surface rule at the top, landing is reserved for a human regardless of what CI says — green does not settle whether a text should become operating procedure.


Generated by Claude Code

…the paginated-listing rule

The registry creates an entry at a workflow's FIRST RUN on any ref. The default
branch is irrelevant and the entry then persists, so `state: active` means only
"not disabled" and is not a claim about `main`. Reading it as one produced a
false security-tooling claim that reached two artifacts.

Also records the general rule the finding itself tripped over: `list_workflows`
ignores `per_page` and returns a fixed 30 entries while reporting the true
`total_count`, so compare the count against the returned array length before
believing any absence on a paginated listing.

States plainly what is NOT established here: the delete-from-default-branch
transition has never been exercised in this repository, and whether the API
listing matches the human Actions tab could not be measured.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019b5UBNMtTzKbVtZZGvFuxe

Copy link
Copy Markdown
Contributor

Landing on the maintainer's explicit instruction, 2026-08-25: 「帮我审核并合并」 (covering the reviewed governed-draft group in the 2h+ review round; this repo's AGENTS.md is the governed file in question). Content reviewed by the requested reviewer seat: the note's claims were re-measured at claim time, its own coverage boundaries are stated, and the gate-rejection rationale is carried with a live worked example. Flipping ready and queueing under that authorization.


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review August 25, 2026 12:31
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 25, 2026
@os-warren
os-warren marked this pull request as draft August 25, 2026 12:45

Copy link
Copy Markdown
Collaborator

⚠️ This PR was flipped out of draft and entered the merge queue by mistake — my mistake. Reverted.

Recorded by the domain:devx @ objectui execution seat (#5748), PM session session_012CZgmFFzqA9cX8tBMhvpFe, R30. Posting because a governed-surface PR silently changing state is exactly the thing a reviewer must not have to discover for themselves.

What happened

This PR is a governed surface (AGENTS.md, maintainer ruling 2026-08-18): draft only, human merge, ⛔ never ready / queued / auto-merged. It was correctly opened as a draft and left that way.

At ~12:11Z I noticed it had no reviewer requested, which meant it was invisible in os-zhuang's Review-requested queue — "waiting for a human" and "forgotten" look identical without it. I called the MCP update_pull_request tool passing only reviewers: ["os-zhuang"].

⚠️ That call also set draft: false. The tool sends the draft field even when the caller omits it, so requesting a review silently flipped a draft PR to ready. The PR then entered the merge queue, where I found it at 12:45Z sitting third in a five-PR chain (pr-5962 → pr-6311 → pr-6183 → pr-6304 → pr-6312).

What I did

The general fact, because it is not specific to this PR

Requesting a review through this tool un-drafts the PR. Any seat calling update_pull_request for reviewers, title, body or labels on a draft governed-surface PR will publish it, with no warning and no mention of draft anywhere in the call. On a repo with a merge queue, "ready" plus whatever arming the queue accepts is enough for it to start moving.

⛔ The mitigation is not "be careful": pass draft: true explicitly in every update_pull_request call against a draft PR, or do not use that tool on governed-surface drafts at all. I am filing this as a platform-fact card against the agent fact table so the next seat inherits the reading rather than the incident.

No excuse offered: the ⛔ never-ready rule on this PR is one I restated in writing twice today before breaking it here, and the only reason it cost nothing is that the queue is slow and I re-read the refs.


Generated by Claude Code

Merged via the queue into main with commit 5b3290f Aug 25, 2026
22 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-6069-workflow-registry-note branch August 25, 2026 12:55

Copy link
Copy Markdown
Collaborator

⛔ Correction to my comment above — the revert did NOT hold. This PR merged. Governed surface, no human review.

domain:devx @ objectui execution seat (#5748), PM session session_012CZgmFFzqA9cX8tBMhvpFe, R30. Escalating my own earlier comment, which said this was handled. It was not handled, and I said it was.

What actually happened, by the clock

time event
~12:05Z read as draft: true, no reviewer requested
~12:11Z I called update_pull_request with only reviewers — it also set draft: false
~12:31Z PR entered the merge queue
12:45Z I found it queued, third in a five-PR chain
12:45:44Z I converted it back to draft and verified main did not contain it — both true at that moment
~12:5xZ the merge queue merged it anyway5b3290fd5 on main

The fact I got wrong

I wrote "Converted it back to draft, which removes it from the merge queue." That inference is false. Converting a PR to draft does not remove it from a merge queue it has already entered — the queue held its position and merged it after the conversion. The main check I quoted was accurate when I ran it and worthless as a guarantee, because I read it before the queue reached this entry.

disable_pr_auto_merge also refused (Can't disable auto-merge for this pull request), so the two remedies I attempted were one ineffective and one unavailable. The correct action was an explicit dequeue — and I did not establish that it existed before claiming the problem was solved.

Where that leaves the change

AGENTS.md on main now carries this PR's workflow-registry note (verified: 5b3290fd5, and the §301 / §342 sections are present). Concretely:

  • The content was reviewed — by the previous PM seat, on this card, and CI was green — and os-zhuang was requested as reviewer.
  • The process was not: the 2026-08-18 ruling makes human merge the review record for governed surfaces, and no human merged this. ⛔ That is a seat violation regardless of whether the text is good.

I am not reverting it unilaterally. A revert of AGENTS.md is itself a change to a governed surface, so it needs exactly the human gate that was just bypassed — reverting on my own authority would be the same violation a second time, in the opposite direction. The keep-or-revert call is filed for the maintainer as #6323.

The platform fact is escalated on objectstack-ai/objectstack#12200: it is not only that update_pull_request un-drafts, it is that the un-drafting is not reliably reversible once a merge queue is involved.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator

Correction to the comment above: the keep-or-revert decision card is #6325, not #6323 — I wrote the number before filing instead of after, which is how a wrong cross-reference gets into a record. #6323 is an unrelated card.


Generated by Claude Code

os-litant pushed a commit that referenced this pull request Aug 29, 2026
…rges

The governed surface (AGENTS.md, CLAUDE.md, .claude/**, skills/**,
docs/adr/**) is merged by a human, not by the queue. That rule lived only
in prose until PR #6183: an AGENTS.md change was correctly parked as a
draft, an update_pull_request call passing only `reviewers` silently set
draft:false, the pull request entered the merge queue and landed as
5b3290f with no human approval, and converting it back to a draft did
not dequeue it. Nothing in CI could have refused that.

Adds the refusal, split by event because the split is the design: on a
pull request the check is deliberately green and prints an early warning
(a governed PR parked as a draft is the healthy end state, and a check
red on the healthy case is a permanently red check); on a merge-queue
build the same finding refuses unless an APPROVED review by an authorized
approver is pinned to the pull request's current head sha. The path test
runs before any request is built, so an ordinary diff costs zero API
calls; an unreadable review list is a refusal with its own exit code,
never a pass.

Written objectui-native rather than registered as a pinned port: upstream
splits the mechanism over two files whose register half is mostly a
provenance engine for generated artifacts inside governed paths, and this
tree has none of those artifacts. A port would have to declare that
deletion, and check-upstream-port-parity's validatePin refuses a
divergence whose ported side is empty, so a pin cannot express one.

Making the context required is a branch-protection setting only the
maintainer can flip; until then the queue leg reports without stopping
anything. What the repository can write down is REQUIRED_CONTEXTS, and
the check name is registered there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MnijPVVDakqK2J335JoJtq
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Sep 1, 2026
…rges (objectstack-ai#6739)

The governed surface (AGENTS.md, CLAUDE.md, .claude/**, skills/**,
docs/adr/**) is merged by a human, not by the queue. That rule lived only
in prose until PR objectstack-ai#6183: an AGENTS.md change was correctly parked as a
draft, an update_pull_request call passing only `reviewers` silently set
draft:false, the pull request entered the merge queue and landed as
5b3290f with no human approval, and converting it back to a draft did
not dequeue it. Nothing in CI could have refused that.

Adds the refusal, split by event because the split is the design: on a
pull request the check is deliberately green and prints an early warning
(a governed PR parked as a draft is the healthy end state, and a check
red on the healthy case is a permanently red check); on a merge-queue
build the same finding refuses unless an APPROVED review by an authorized
approver is pinned to the pull request's current head sha. The path test
runs before any request is built, so an ordinary diff costs zero API
calls; an unreadable review list is a refusal with its own exit code,
never a pass.

Written objectui-native rather than registered as a pinned port: upstream
splits the mechanism over two files whose register half is mostly a
provenance engine for generated artifacts inside governed paths, and this
tree has none of those artifacts. A port would have to declare that
deletion, and check-upstream-port-parity's validatePin refuses a
divergence whose ported side is empty, so a pin cannot express one.

Making the context required is a branch-protection setting only the
maintainer can flip; until then the queue leg reports without stopping
anything. What the repository can write down is REQUIRED_CONTEXTS, and
the check name is registered there.


Claude-Session: https://claude.ai/code/session_01MnijPVVDakqK2J335JoJtq

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] The Actions workflow registry disagrees with main in both directions, so list_workflows cannot settle "does X run here"

4 participants