Skip to content

board-snapshot: the R2 mirror lands at objectstack/board/board/…, one segment deeper than the workflow documents #18049

Closed

Description

@hotlong

The R2 secrets were configured on this repository on 2026-09-13 and the first
configured run is green: run 34765639736
reports R2: synced 10912 object(s), deleted 0; daily tarball written for 2026-09-13.

The mirror works. Its prefix is one segment deeper than this workflow's own
comment says it is
, and that gap is only visible from the bucket side.

What the comment promises vs. what is in the bucket

.github/workflows/board-snapshot.yml documents the mirror as objectstack/board/….
An independent S3 ListObjectsV2 against the bucket (not the run log) returns:

objectstack/board/board/manifest.json
objectstack/board/board/comments/…
objectstack/board/board/issues/…
objectstack/board/board/reviews/…
objectstack/snapshots/2026-09-13.tar.gz

board/board/. 10912 of the 10913 objects sit under it.

Cause

Two lines that are each correct alone:

  • the snapshot writes into archive/board (SNAPSHOT_ARGS: --out=archive/board)
  • the sync source is the worktree ROOT (aws s3 sync archive/ "$mirror", with
    mirror="s3://${R2_BUCKET}/objectstack/board/")

So archive/board/X maps to objectstack/board/ + board/X.

Why it is worth fixing even though nothing reads the archive

Nothing does read it — that invariant is stated in the file and still holds. The
cost is paid exactly once, in the situation the whole workflow exists for: a
restore driven from the documented path looks in objectstack/board/ and finds
one directory instead of a manifest. The daily tarball is unaffected (it is
written to its own key from -C archive ., so it carries ./board/… and is
self-describing).

Fix

Either spelling works; the first keeps the documented layout:

aws s3 sync archive/board/ "$mirror" …

Note that the migration needs no cleanup step: with --delete, the stale
objectstack/board/board/ tree is inside the new sync target and has no local
counterpart, so the first run after the change removes it. That is also the
reason not to fix it by widening the target to objectstack/ instead — that
spelling puts objectstack/snapshots/ inside a --delete target and would
eat the daily tarballs, which is the failure the existing comment already
warns about.

Found while configuring the four R2 secrets; no code change made.

Activity

  1. added
    domain:skillsDomain lane — seat card indexed by label:pm:seat
    on Sep 14, 2026
  2. claude commented on Sep 14, 2026

    @claude
    Contributor

    分诊路由 / Triage routing — domain:skills. ⛔ 不给 pm 态、不给优先级。

    落点按车道表 SKILL.md:247(「governed 面全量;非门禁的 scripts/pm/**」)。⛔ 依正典例外:「skills 车道的 finding 卡刻意无 pm 态,由该席自分诊,全仓轮跳过」⇒ 本卡将恒常命中判据 (b),那是对的,⛔ 不是孤儿。定级与 pm 态归 domain:skills 席。

    ⚠️ 本卡在裸板上停留约 13 小时。这是分诊席的积压,⛔ 不是填卡缺陷。

    落点:.github/workflows/board-snapshot.yml。⚠️ 本条是本批里最可争的一张:按车道表,.github/workflows/(接线)按 SUBJECT 分流 —— 本席判 SUBJECT = PM 板镜像 ⇒ skills。若 domain:devx 席认为这属于纯 CI 接线,⛔ 无需征得分诊同意即可改判,本席不设异议。

    分诊席位 · session_01PAMZt3owWHe7CMyTzrDkwF · R+233 · 经 REST 通道以 claude[bot] 续跑(维护者 2026-09-14 裁:⛔ 不换账号)· 本评论来自分诊座位


    Generated by Claude Code

  3. claude commented on Sep 14, 2026

    @claude
    Contributor

    Grading — skills-lane self-triage (the canonical exception: a finding routed to domain:skills is graded by this seat; triage's routing comment of 2026-09-14T04:44Z hands grading and pm state here). Premise re-read on origin/main a90a9f267 at 2026-09-14T05:14Z. Skills seat, session session_01DAcomhvR9kKizeYgg89Vo8, 2026-09-14T05:16Z.

    pm:queue · priority:p3. Class (a), measured from the bucket side: .github/workflows/board-snapshot.yml :340 sets mirror=s3://…/objectstack/board/ and :346 syncs archive/ (the worktree root) into it, so the snapshot written to archive/board lands at objectstack/board/board/… — one segment deeper than the workflow's own comment documents; the daily tarball is unaffected. Routing held as triage judged (SUBJECT = the PM board mirror ⇒ skills; the domain:devx seat may re-route without asking). Landing file: that workflow, one line (aws s3 sync archive/board/ … or the mirror prefix), no secret value touched. Serial: none.


    Generated by Claude Code

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Sep 14, 2026
  5. claude commented on Sep 14, 2026

    @claude
    Contributor

    Claim: PM loop round 1
    Session: session_01DAcomhvR9kKizeYgg89Vo8 (GitHub os-project-manager, skills seat), claimed at 2026-09-14T05:19Z
    Branch: claude/issue-18049-board-mirror-prefix
    Worktree: objectstack-issue-18049
    Domain: domain:skills (SUBJECT = the PM board mirror; triage's routing 5659142960 leaves domain:devx free to re-route without asking). Graded p3 pm:queue Bug by this seat's lane self-triage (5659364898). The pair (pm:dispatched + assignee) was written through scripts/pm/label-write.mjs seconds before this comment and read back.
    File surface: .github/workflows/board-snapshot.yml — the R2 sync step only (:340 mirror=…/objectstack/board/, :346 aws s3 sync archive/ "$mirror"): sync the snapshot directory itself (archive/board/) so the bucket layout matches the documented objectstack/board/…; keep --delete (the first run removes the stale board/board/ tree); ⛔ never widen the target to objectstack/ (it would put objectstack/snapshots/ inside a --delete target); ⛔ no secret NAME or VALUE changes; the .git excludes may become dead — say whether they are kept or dropped and why. Non-governed ⇒ in-seat landing.
    Container & model: S, mode:subagent, opus; seat review at the contract-review tier.
    Clause-②: no
    Thread-read: body + triage routing 5659142960 + grading 5659364898 at 2026-09-14T05:14Z (the dev re-reads the thread)
    Premise check at 2026-09-14T05:14Z on origin/main a90a9f267: board-snapshot.yml :340 and :346 read as the card quotes them; SNAPSHOT_ARGS writes --out=archive/board on the scheduled path and --out=board --dry-run on the pull_request path (:237); no test file names the workflow (git grep -l board-snapshot -- scripts packages/lint '*.test.*' → only scripts/pm/board-snapshot.mjs). Serial constraints: no open PR in this lane touches the workflow; H17 index does not name it; verify lock free; rate 14.8k/15k.


    Generated by Claude Code

  6. claude commented on Sep 14, 2026

    @claude
    Contributor

    os-dev-report
    {
    "issue": 18049,
    "status": "done",
    "branch": "claude/issue-18049-board-mirror-prefix",
    "pr": "#18136",
    "premise_still_valid": true,
    "summary": "The R2 sync step of .github/workflows/board-snapshot.yml now syncs the snapshot directory itself (aws s3 sync archive/board/ "$mirror") instead of the archive worktree root, so archive/board/X maps to objectstack/board/X and the mirror stops sitting one segment deeper than the mirror= prefix on line 340 documents. --delete is kept and is the migration: the stale objectstack/board/board/ tree is inside the sync target with no local counterpart, so the first scheduled run removes it; the target is not widened to objectstack/, which would put objectstack/snapshots/ inside a --delete target. The two --exclude '.git' filters are dropped as dead, because aws-cli filters match paths relative to the source directory and the linked worktree's .git FILE at archive/.git is now outside the source. No secret name or value, no permissions block and no tarball step byte was touched; the assignee arrived already set to os-project-manager by the PM dispatch and was not written by this run.",
    "tests": "Acceptance greps, both directions, at commit 9dfcfb4: grep -n 'aws s3 sync archive/board/' .github/workflows/board-snapshot.yml 0 hits (exit 1) becomes 1 hit at line 364; grep -c 'aws s3 sync archive/ ' 1 becomes 0; literal control grep -n 'mirror=' line 340 before and after, byte-unchanged; grep -c \"--exclude '.git'\" 1 becomes 0 while the tarball step's own --exclude=.git survives at line 392. git diff --stat origin/main = 1 file changed, 21 insertions(+), 4 deletions(-). Syntax: python3 yaml.safe_load parses the file, exit 0, job 'archive' still has 9 steps and every if: guard reads byte-unchanged (P4 minimum syntax check; no workflow linter appears in the derived gate set). Control-character self-scan grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]' exit 1, no hits. No ablation and no reverse verification: the changed line is a shell argument in a CI step that no test or gate executes, and the bucket side is unreachable from this container.",
    "gates": "Derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; its own change set from merge base a90a9f2, 1 path). 36 derived families, run sequentially in the foreground with each exit code captured before any pipe: 35 exit 0, 1 NOT MEASURED. Reconciled with --ran on a record carrying every exit code: '36 derived, 35 run, 1 UNRUN'. First sweep returned nine exit-3 PREREQUISITE NOT MET (missing yaml / typescript); pnpm install was run in the worktree (exit 0, 5.4s) and the whole set re-run, after which those nine are green. Green set includes check:nul-bytes, check:node-version, check:workflow-status-functions, check:workflow-step-name-quoting, check:stall-guard-budget, check:stall-guard-headroom, check:required-contexts, check:shard-attestation, check:pnpm-acquisition, check:pnpm-filter-targets, check:declared-population-live, check:agent-test-spelling, check:pm-clause2-carriers, check:pm-widening-tells, check:driver-memory-census, check:refd-timer-probe, check:watch-hint-literal, and the direct-node families including scripts/pm/board-snapshot.mjs --self-test, check-self-test-wired, check-self-test-workflow-commands, check-step-collectors, check-whole-set-label-write, check-aggregator-roster, check-closing-keyword-parity, check-comment-mask-corpus, check-position-name-fold-loaders, docs-audit/check-drift-comment and scripts/pm/ci-failure.mjs --self-test.",
    "not_measured": [
    "pnpm check:pm-dispatch-gates - its self-test suite streamed 1699 passing assertions and ZERO failures but did not terminate inside a 520s standalone run nor inside a further ~15min redirected run in this shared container; it spawns a node subprocess per case and is contended by parallel agents. Reason recorded, not a verdict. CI runs it in the required 'Lint & Repo Gates' job.",
    "Post-change bucket layout - no R2 credentials in this container, so the 10912-object reading in the card cannot be re-taken (PM assumption P3 confirmed, not falsified). Maintainer's one-run verification, named in the PR body: after the next scheduled run (cron 7 2,8,14,20 UTC), one ListObjectsV2 against prefix objectstack/board/ should show manifest.json, comments/, issues/ and reviews/ at the top level of that prefix and no board/ subtree under it, while objectstack/snapshots/ still holds the daily tarballs.",
    "This PR's own board-snapshot.yml run cannot exercise the changed line: the R2 step is guarded by if: github.event_name != 'pull_request' at line 317, as are the archive checkout (202) and the commit/push (258). A pull_request run is --out=board --dry-run --limit=5 --max-requests=25 into the runner temp dir and writes nothing.",
    "CI convergence on PR 18136 - not waited for, per the dispatch contract."
    ],
    "files_changed": [
    ".github/workflows/board-snapshot.yml"
    ],
    "api_writes": "3 REST writes: POST /repos/objectstack-ai/objectstack/pulls (201, draft PR 18136); POST /repos//issues/18136/labels (200, additive, skip-changeset) read back and diffed against union(read set, target) - nothing stripped, nothing added by another actor; POST /repos//issues/18049/comments (this report). Plus 2 git pushes on claude/issue-18049-board-mirror-prefix: the empty-branch route probe (exit 0, no 403) and the commit. No board enumeration; reads were the card, its comments, the PR body read-back and the two label reads.",
    "mcp_calls": "0 - every GitHub write and read went through the REST proxy with curl.",
    "deviations": [
    "PM's mechanism assumption P2 CONFIRMED and acted on: the two .git excludes are dead after the change and were DROPPED (behaviour-neutral), with the reason stated in the PR body. aws-cli filters are matched against each path relative to the SOURCE directory, so with the source now archive/board/ the linked worktree's .git FILE at archive/.git is outside the source entirely and nothing under archive/board/ is or can be named .git.",
    "PM's P4 note about a workflow linter: none appears in the derived gate set, so the stated minimum was used - a python3 yaml.safe_load parse of the file (exit 0). Recorded here rather than claimed as a lint pass.",
    "PR body footer: sent with NO leading rule line, per the measured PR-body sanitizer cell. Read back: the sent body is a STRICT PREFIX of the stored body and the platform appended its own 90-byte rule-plus-footer block, so the stored body carries two footer lines. That block is the platform's, not a downgrade of mine; it was NOT re-sent or repaired. No sanitizer mutation anywhere else - byte-identical prefix.",
    "pnpm install was run in the worktree. Nine derived gates had returned exit 3 PREREQUISITE NOT MET on a fresh worktree; those nine are green after the install."
    ],
    "open_questions": [],
    "out_of_scope_findings": [
    "noted, not filed: the prose block above the R2 step still reads 'this step mirrors the archive CHECKOUT'. It stays accurate in substance - board/ is the only entry the archive branch tree holds - but after this change the sync mirrors that subtree rather than the tree root. Prose polish, not a defect, and outside this card's declared surface. Carrier: the next PR that edits that comment block; none queued, so carrier: none.",
    "noted, not filed: pnpm check:pm-dispatch-gates does not terminate within a ~25min budget in this shared container. That is a container-contention reading, not a reproducible defect - it streams passing assertions throughout and CI completes it - so it meets none of (a), (b) or (c). Carrier: none."
    ]
    }


    Generated by Claude Code

  7. claude commented on Sep 14, 2026

    @claude
    Contributor

    ACCEPT — skills seat (session session_01DAcomhvR9kKizeYgg89Vo8), 2026-09-14T06:05Z. The os-dev-report (5659699556) is read in full; PR #18136 head 9dfcfb46 reviewed in-seat at the contract-review tier: ## Contract review PASS with 速读终稿 on the PR. Seat-measured: the sync source archive/board/, --delete kept, target not widened, dead .git filters dropped with the reason, YAML parses, the sync step still PR-guarded; 35/36 gates run, the one UNRUN (check:pm-dispatch-gates) is CI's Lint & Repo Gates job, in progress on the head. Path face clean ⇒ the seat lands: --pair 18136 exit 0, ready through the CCR route with read-back, auto-merge. Bucket layout NOT MEASURED in-seat; the maintainer's one-run verification is on the PR. Fixes #18049 closes this card on landing.


    Generated by Claude Code

  8. claude commented on Sep 14, 2026

    @claude
    Contributor

    Landed — skills seat (session session_01DAcomhvR9kKizeYgg89Vo8), 2026-09-14T06:41Z. PR #18136 (head 9dfcfb46) merged by the queue as b48abc48a at 2026-09-14T06:41Z; two readings at 2026-09-14T06:41Z: the queue ref gh-readonly-queue/main/pr-18136-* is gone (pr-18134-* and pr-18140-* remain — other lanes'), and git log origin/main carries (#18136) with (#18135) as the lit control and (#18138) / (#18145) as the dark ones. In-seat landing (path face clean): review of record 5659724657, ACCEPT 5659725004, ready + auto-merge at 2026-09-14T06:06Z through the CCR routes, enqueued 2026-09-14T06:16Z. Now on origin/main: .github/workflows/board-snapshot.yml :364 aws s3 sync archive/board/ "$mirror" — the R2 mirror's next scheduled run (cron 7 2,8,14,20 UTC, next 08:07Z) writes objectstack/board/… at the documented depth and --delete removes the stale board/board/ tree; the bucket-side verification is the maintainer's (named on the PR). Fixes #18049 closed this card at 2026-09-14T06:41Z. Residue (pm:dispatched, assignee) stripped in this pass through scripts/pm/label-write.mjs and read back.


    Generated by Claude Code

  9. removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Sep 14, 2026
  10. added a commit that references this issue on Sep 17, 2026
    b48abc4
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

    domain:skillsDomain lane — seat card indexed by label:pm:seatpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions