Skip to content

Scenario library T8: export verified scenario apps to a new gallery repository — source, run record, auto screenshots and the 8–10 问数 transcripts, rebuilt per release #30349

Open

Description

@objectstack-fleet

epic:#3600#1 · hotlong · session_01Q4EjjPXwX1YfHyKJUh4agu

Filing-gate category: ④ coordination child of objectstack-ai/cloud#3600 (scenario-library epic), item T8. Maintainer-directed (verbatim, Claude Code session 2026-11-05): 「画廊 可以新开仓库」 · 「画廊是主要的引流手段 ,repo:www.objectos.ai 需要有配套的落地页面」.

Blocked-by: #30795

(#30292, the runner, landed as PR #30746. The exporter now waits for the demo + card code namespace of #30795, ruled on objectstack-ai/cloud#3600 comment 6676044775.)

Business rationale:

  • The maintainer names the gallery the primary acquisition channel.
  • A gallery made of verified, regenerated scenario apps cannot rot the way the v15-pinned templates repo did: every framework release rebuilds it.
  • Its proof artifacts double as the landing pages' content on www.objectos.ai (a sibling card there consumes them).

Scope

  1. An exporter on top of the local runner (Scenario library T3/T4: local Claude Code runner — Build, Run (问数 + operations per persona over the app's MCP), Iterate — writing run records, zero cloud-billed tokens #30292). For every scenario card whose run record passes all three halves (Build, Run-问数, Run-operations), write to a new gallery repository:
    • the generated app's metadata source;
    • its run record and scorecard;
    • auto-captured screenshots (Playwright against the booted app: list, kanban or calendar where tagged, record page, dashboard);
    • the card's 8–10 问数 Q&A transcript, with per-persona answers, so a reader sees the permission boundary hold.
  2. Only passing cards are exported. A failing card stays out, and its failure goes to the gap radar (T6).
  3. Rebuilt on each framework release. Exporting the same card again replaces the old output, so the gallery never pins an old version.
  4. A machine-readable index (one JSON manifest: scenario id, function, title zh/en, screenshot paths, Q&A count, framework version, run date). The www.objectos.ai landing pages build from it.
  5. Card content is generic only. Per the D1 ruling, ⛔ no customer-derived scenario is ever exported.

Preconditions (maintainer-side)

  • The gallery repository must be created by the maintainer. A seat creates no repositories. Suggested name: objectstack-ai/gallery; the maintainer decides.
  • It is a generated-output repository. It needs a fleet seat only if it gets standing work, so a write path for the exporter (a release workflow or the relay) is part of this card's design.

Out of scope

  • The www.objectos.ai landing pages (sibling card in that repo).
  • The "try it in ObjectOS cloud" deep link (pending decision Q6 on cloud#3600).
  • The templates repo's fate (pending on cloud#3600).

Reader: the epic PM of cloud#3600 dispatches this once #30292 merges and the gallery repository exists.


Generated by Claude Code

Activity

  1. added
    domain:devxDomain lane — seat card indexed by label:pm:seat
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    pm:epicReserved by a dedicated epic PM, parent or sub-issue; other PMs never take it; never with pm:queue
    area:devpathThe road — create, dev, verify, publish/install, connect an agent, iterate
    on Nov 5, 2026
  2. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: first-touch audit · p2 · epic child · domain:devx kept · pm:blocked kept · 2026-11-05T07:59Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#3600#1 · hotlong · session_01Q4EjjPXwX1YfHyKJUh4agu

    Precondition met: the maintainer created the gallery repository objectstack-ai/gallery (verbatim on objectstack-ai/cloud#3600: 「画廊仓库叫 gallery,加模板定制跑法,已创建仓库 https://github.com/objectstack-ai/gallery」). The seat read it on 2026-11-05: git ls-remote answers with no refs, so it exists and is empty. The exporter's write path into it (a release workflow or the fleet relay) is still this card's design item. State unchanged: pm:blocked on #30292.

  4. added
    priority:p1High: required for production / M2
    and removed on Nov 5, 2026
  5. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#3600#1 · hotlong · session_01Q4EjjPXwX1YfHyKJUh4agu

    Re-graded p2 → p1 on a maintainer ruling (Claude Code session 2026-11-05, recorded on objectstack-ai/cloud#3600), verbatim: 「都可以标记高优先级」, then on the seat's follow-up question the option 「p1 + 只交 6 张卡 (推荐)」. This card stays in the epic subtree (pipeline contract), so pm:epic is unchanged. Note for triage: this is a direct maintainer re-grade. NORTH-STAR's "re-grade by the road table, never card by card" yields to the ruling by the skill's precedence (maintainer ruling > North Star).

  6. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#3600#1 · hotlong · session_01Q4EjjPXwX1YfHyKJUh4agu

    objectstack-ai/gallery was initialized at the maintainer's request (「这个项目你应该加入进来,帮我初始化」): commit 09ce9b06 on main.

    • Hand-written files: README (bilingual, trust-first positioning), AGENTS.md + CLAUDE.md, Apache-2.0 LICENSE, .gitignore, apps/README.md.
    • Agent rules: the repository is generated output, and only the exporter writes apps/** and index.json. Only scenarios that pass every half are exported. ⛔ No customer-derived content.
    • This card still owns: the exact apps/<scenario>/ layout, the index.json format (the README promises: function, title, screenshots, question count, framework version, run date), and the write path into the repository (a release workflow or the fleet relay).
  7. removed
    pm:epicReserved by a dedicated epic PM, parent or sub-issue; other PMs never take it; never with pm:queue
    on Nov 6, 2026
  8. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#3600#1 · hotlong · session_01Q4EjjPXwX1YfHyKJUh4agu

    Handed to the domain:devx lane. Maintainer, verbatim (Claude Code session, 2026-11-06): 「能交给车队开发的你不要自己开发」. pm:epic is removed so the lane seat claims it from the queue; the epic PM no longer dispatches it. State stays pm:blocked on its Blocked-by: lines. The namespace for exported apps is ruled: demo + the card code (#30795). The epic PM still answers rulings and cross-repo questions on objectstack-ai/cloud#3600.

  9. added
    pm:queueReady for the PM dispatch loop
    and removed
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    on Nov 6, 2026
  10. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#3600#1 · hotlong · session_01Q4EjjPXwX1YfHyKJUh4agu

    Blocked-by: #30795 closed (PR #30837 merged as c1f311f8): cards carry an app code, and every scenario app is built in the demo + code namespace. pm:blocked → pm:queue for the domain:devx lane. The exporter writes into objectstack-ai/gallery (initialised; read its AGENTS.md: only scenarios that passed every half, no customer-derived content). Gallery apps are published browse-hidden (objectstack-ai/cloud#3649) once that lands.

  11. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 6, 2026
  12. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#2 · marchtian · session_017ihL2ebbZkr2QAEG1x7UC8

    Claim: PM loop round 1
    Session: session_017ihL2ebbZkr2QAEG1x7UC8
    Account: marchtian
    Branch: claude/issue-30349-gallery-exporter
    Worktree: objectstack-issue-30349
    Domain: domain:devx
    Seat: domain:devx#2
    File surface: a gallery exporter in packages/qa/scenarios (a new src/export/ module and a CLI entry next to src/runner/cli.ts). It reads run records the local runner wrote and exports only scenarios that passed every half. For each one it writes the apps/<scenario>/ layout: the generated app's metadata source, the run record and scorecard, the per-persona 问数 transcript, and screenshots captured by Playwright against the booted app. It also writes the index.json manifest. Re-export replaces the old output. It refuses any card that is not generic. Tests under packages/qa/scenarios/test/ cover these with doubles, and the package README gets a section once #30846 lands. Plus whatever pin or self-test a gate derives from those files. ⛔ This PR writes nothing to objectstack-ai/gallery and adds no workflow. The write path into the gallery (a release workflow or the relay) is a design item, so it goes to the maintainer before any cross-repo write.
    Container & model: S, mode:subagent
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a — QA tooling (scenario pipeline T8); independent contract review before ACCEPT
    Thread-read: 6680596482
    Premise: holds. origin/main 6ff27b348e has the runner (#30292, PR #30746) and the demo + card code namespace (#30795, PR #30837), and no exporter. objectstack-ai/gallery is at 09ce9b0 with only the hand-written files. Its AGENTS.md says only the exporter writes apps/** and index.json, that it exports only scenarios that pass every half, and that nothing customer-derived goes in.
    Serial constraints cleared: #30846 (#30802) touches loader.ts, two cards, the package README and refusals.test.ts. This card does not touch loader.ts, the cards or refusals.test.ts, and holds its README section until #30846 lands.

  13. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#2 · marchtian · session_017ihL2ebbZkr2QAEG1x7UC8
    os-dev-report

    {
      "issue": 30349,
      "status": "done",
      "branch": "claude/issue-30349-gallery-exporter",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/30930",
      "head": "7d21d93b92",
      "session": "session_017ihL2ebbZkr2QAEG1x7UC8",
      "premise_still_valid": true,
      "summary": "A gallery exporter in @objectstack/scenarios (src/export/, CLI export-gallery --runs DIR --out DIR) reads the local runner's run records, projects and recording, and writes every verified app to a local directory in the gallery layout: source, run record, scorecard, per-persona 问数 transcript (Markdown + JSON), screenshots, and a zod-validated index.json. It writes nothing to objectstack-ai/gallery and adds no workflow. The card schema has no generic marker, so the CLI's guard refuses every card today and the gallery stays empty until the marker is decided.",
      "change": "New module packages/qa/scenarios/src/export/ (record.ts, pass.ts, generic.ts, source.ts, transcript.ts, manifest.ts, screenshots.ts, exporter.ts, cli.ts, index.ts), package export ./export and script export-gallery, dependency playwright-core 1.63.0 (already in the lockfile; 3-line importer entry). Runner touch-ups with no behaviour change: orchestrate.ts moves the seed loop into an exported seedApp (reused by the screenshot step) and adds modeDir(card, mode); runner/cli.ts exports ADMIN and findUp.",
      "layout": {
        "apps/SCENARIO/README.md": "title, requirement (prompt.short zh/en), each owed half's verdict, the scorecard table (renderScorecard), file links, skipped-shot reasons",
        "apps/SCENARIO/source/": "the authored metadata source, allow-list: objectstack.config.ts, objectstack.manifest.json, package.json, tsconfig.json, and the whole src/ tree (regular files; never node_modules, dist, .objectstack, .git; a symlink refuses the export). Not copied: .agents/.claude skills, .env, README.md, Dockerfile, docker-compose, .github, lockfiles. It is the scratch project after the change request (the app the iterate halves graded).",
        "apps/SCENARIO/run-record.json": "the newest run record as read, every occurrence of the local work directory replaced by $WORK",
        "apps/SCENARIO/scorecard.json": "record.scorecard (work dir redacted)",
        "apps/SCENARIO/scorecard.md": "renderScorecard(record.scorecard)",
        "apps/SCENARIO/transcript.json": "card questions (ask zh/en, object, personas), personas (id, role), and per Run half (scratch/run, scratch/iterate-run, template/iterate-run) every turn: question, persona (null = admin), truth, answer (replyText, toolCalls name/input/output/isError), grade, notMeasured, error",
        "apps/SCENARIO/transcript.md": "the same, per half, per question, per asker: expected, grade line, quoted reply, tool calls",
        "apps/SCENARIO/screenshots/KIND.png": "KIND in list, kanban, calendar, record, dashboard",
        "index.json": "the manifest (index_schema)"
      },
      "index_schema": {
        "schemaVersion": "literal 1",
        "apps": "array sorted by id, each id once (refine); strict objects throughout; no export timestamp, so an unchanged re-export is a no-op diff",
        "apps[].id": "card.id",
        "apps[].code": "card.code",
        "apps[].namespace": "scenarioNamespace(card) = demo + code",
        "apps[].path": "apps/ID (refine: equals apps/ + id)",
        "apps[].function": "card.category (CategorySchema, the marketplace taxonomy)",
        "apps[].title.en": "card.title",
        "apps[].title.zh": "null: the card schema has no Chinese title (open question)",
        "apps[].summary": "card.prompt.short {en?, zh?}",
        "apps[].capabilities": "card.capabilities, sorted",
        "apps[].screenshots[]": "{kind, path apps/ID/screenshots/KIND.png, object?, view?} from the screenshot step's taken shots",
        "apps[].qa": "{questions: record.cardRun.counts.questions, turns: counts.questionTurns, personas: counts.personas}",
        "apps[].operations": "record.cardRun.counts.operations",
        "apps[].framework.version": "the generated project's package.json @objectstack/spec pin (create-objectstack pins exactly); missing = refusal",
        "apps[].framework.runner": "record.env.framework (the objectstack checkout's short sha, or unknown)",
        "apps[].runDate": "record.date",
        "apps[].run": "record.run"
      },
      "screenshots": {
        "boot": "the runner's own bootDev(projectDir) (objectstack dev --fresh --no-watch, wait for /api/v1/health), HttpApp.signInAdmin, then seedApp(namespacedScenario, app, []) — the runner's seed loop, every user handle as the admin; then a Chromium (playwright-core, --chromium PATH or PLAYWRIGHT_BROWSERS_PATH/chromium) signed in through POST /api/v1/auth/sign-in/email in the browser context; booted.stop() in finally",
        "views": "list = first expect.appNavTargets object the build declares, else first expect.objects (/_console/apps/APP/OBJECT); record = that object's first seed row (/record/ID); dashboard = an expected dashboard the build declares, else the build's first (/dashboard/NAME); APP = the build's app prefixed with the namespace, else its first",
        "kanban_calendar_from_tags": "taggedViewKinds: a capability tag whose name part matches /kanban/ or /calendar/ (e.g. records-forms.kanban-drag-persistence, records-forms.calendar-interactions); for each tagged kind, a list view of that type the build declares (containers read with the spec's deriveViewContainerObject + expandViewContainer), the list object's own first: /OBJECT/view/OBJECT.KEY. No card of the open library tags either today.",
        "skips": "a tagged view the build lacks, a build with no dashboard or no app, a refused seed row for the record shot: skipped with the reason (README + CLI summary); a step that throws refuses the app",
        "flag": "--no-screenshots skips the step entirely (nothing booted)",
        "tests": "view selection and capture with doubles (test/export/screenshots.test.ts, 12 tests)",
        "real_boot_reading": "one-off, not a permanent test: npx create-objectstack@latest (18.3.0) + a hand-written fixture app (no model available), os build exit 0, exportGallery with the real captureScreenshots/bootDev/HttpApp/playwrightBrowser: 4 shots (list, kanban, record, dashboard), 0 skipped, PNGs inspected. The released 18.3 Console shows a default-currency dialog over the first app page once per session; the adapter visits the first shot's page once unshot (dialog retired on main by the first-run checklist changeset)."
      },
      "pass_rule": "Newest record per card by date (file-name order breaks a tie). Read record.results verdicts by id CARD.MODE.HALF; nothing re-scored. pass only; partial/fail/blocked/not-run/missing are not a pass. Scratch mode must be present and owes build, run-ask, run-operations, iterate-build, iterate-run-ask, iterate-run-operations (a --no-iterate-run record fails). Template mode, when the record holds it, owes iterate-build, iterate-run-ask, iterate-run-operations; its build/run-ask/run-operations rows are not-run by construction and are not asked; a skipped template mode fails.",
      "generic_guard": "generic.ts: GenericGuard(scenario) returns {generic, reason}, checked first. The CLI uses NO_GENERIC_MARKER, which refuses every card: 'not marked generic: the card schema carries no explicit generic marker ...'. provenance is free prose and the open-library directory is not a marker (the loader reads any directory, the private overlay included). No CLI flag swaps the guard; exportGallery takes it as a parameter only so tests can run the export path with a double. schema.ts/loader.ts untouched. Pinned: the default guard refuses all 9 open-library cards; the CLI end to end over the real card directory exports nothing.",
      "write_path_options": [
        {
          "option": "A. PR-based flow from a local run (recommended first)",
          "how": "Whoever runs the scenario runner locally (it needs a Claude Code subscription login by the cost ruling, so it cannot run in CI) runs export-gallery --out against a gallery checkout, commits on a branch and opens a PR on objectstack-ai/gallery; the maintainer merges.",
          "needs": "push or fork access to objectstack-ai/gallery for that person; no new secret; no Tier H surface in objectstack (no workflow)",
          "risks": "manual cadence (per release by hand); depends on one machine's run; the PNG diff is noisy because screenshots are not byte-stable across boots"
        },
        {
          "option": "B. Gallery-side workflow + deploy key (recommended once cadence matters)",
          "how": "The local run uploads a run bundle (records, recording, project sources without node_modules) as a release asset or artifact; a workflow in objectstack-ai/gallery (or in objectstack) installs the projects, runs export-gallery in CI (the exporter needs no model; Chromium boots the app), and opens a PR on gallery (preferred) or pushes main.",
          "needs": "a deploy key or fine-grained token with contents:write on objectstack-ai/gallery only, stored as a secret where the workflow runs; a new workflow file (Tier H if it lives in objectstack's .github/workflows; maintainer-owned if in gallery); a bundle format",
          "risks": "a write credential to a public repo; the bundle is a trust boundary (CI must re-run the generic guard, the pass rule and the transcript re-grade, which the exporter already does); must not be a release act (Prime Directive 15: trigger after publish or by the maintainer, never by a seat dispatching release.yml)"
        },
        {
          "option": "C. Fleet relay with a new op (not recommended)",
          "how": "A new fleet-write op (e.g. gallery_publish) packs the exported tree into the repository_dispatch payload; fleet-write.yml writes blobs/tree/commit/ref on objectstack-ai/gallery as objectstack-fleet[bot] and opens a PR.",
          "needs": "the fleet App installed on objectstack-ai/gallery with contents and pull-requests write; a new op in scripts/pm/fleet-write/ops.mjs + validate.mjs; likely a change to .github/workflows/fleet-write.yml (Tier H)",
          "risks": "repository_dispatch client_payload is small, so screenshots cannot ride it (would need an artifact side-channel); the relay gains a generic write-any-tree op on a public repo, a large blast radius; seats would be writing generated content to a repo the card says only the exporter writes"
        }
      ],
      "tests": "pnpm --filter @objectstack/scenarios: tsc --noEmit exit 0; vitest run exit 0 'Test Files 20 passed (20) · Tests 278 passed (278)' on 7d21d93b92. New: test/export/exporter.test.ts (21) on a real runCard run of the real crm card (fixture.ts: real project dirs, in-memory app, RecordingDriver), test/export/screenshots.test.ts (12). No ablation: no previously tested behaviour changed (seedApp is a move; existing orchestrate tests stay green).",
      "gates": [
        "node scripts/check-closing-keyword-parity.mjs :: exit 0",
        "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0",
        "node scripts/check-comment-mask-adoption.mjs :: exit 0",
        "node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0",
        "node scripts/check-comment-mask-corpus.mjs :: exit 0",
        "node scripts/check-console-intercept-disarm.mjs :: exit 0",
        "node scripts/check-console-intercept-disarm.mjs --self-test :: exit 0",
        "node scripts/check-dts-emitted.mjs --self-test :: exit 0",
        "node scripts/check-issue-citations.mjs :: exit 0",
        "node scripts/check-osv-exemptions.mjs :: exit 0",
        "node scripts/check-osv-exemptions.mjs --self-test :: exit 0",
        "node scripts/check-plugin-teardown-shape.mjs :: exit 0",
        "node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0",
        "node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0",
        "node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0",
        "node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0",
        "node scripts/docs-audit/check-affected-docs.mjs :: exit 0",
        "node scripts/docs-audit/check-drift-comment.mjs :: exit 0",
        "node scripts/osv-base-relative.mjs --self-test :: exit 0",
        "pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0",
        "pnpm --filter @objectstack/spec run check:llms-txt :: exit 0",
        "pnpm check:changeset-fixed :: exit 0",
        "pnpm check:ci-filter-parity :: exit 0",
        "pnpm check:closed-ceremony-text :: exit 0",
        "pnpm check:cross-package-test-inputs :: exit 0",
        "pnpm check:dispatcher-error-vocabulary :: exit 0",
        "pnpm check:doc-authoring :: exit 0",
        "pnpm check:driver-memory-census :: exit 0",
        "pnpm check:dts-closure :: exit 0",
        "pnpm check:dual-build-cjs-loads :: exit 3  (PREREQUISITE NOT MET: no dist; pnpm build, VERDICT command-exit 0, then rerun: exit 0)",
        "pnpm check:engine-double-contract :: exit 0",
        "pnpm check:error-status-conformance :: exit 0",
        "pnpm check:gitlink-declared :: exit 0",
        "pnpm check:issue-citations :: exit 0",
        "pnpm check:keyed-text-bounds :: exit 0",
        "pnpm check:lean-entry-closure :: exit 0",
        "pnpm check:logger-receiver-detach :: exit 0",
        "pnpm check:manifest-repository-directory :: exit 0",
        "pnpm check:merge-driver :: exit 0",
        "pnpm check:nul-bytes :: exit 0",
        "pnpm check:objectql-double-limit :: exit 0",
        "pnpm check:org-identifier :: exit 0",
        "pnpm check:override-consistency :: exit 0",
        "pnpm check:page-declaration-shape :: exit 0",
        "pnpm check:platform-object-tenancy-census :: exit 0",
        "pnpm check:pnpm-flag-spellings :: exit 0",
        "pnpm check:published-files :: exit 0",
        "pnpm check:query-options-erasure :: exit 0",
        "pnpm check:refd-timer-probe :: exit 0",
        "pnpm check:registry-log-declared :: exit 0",
        "pnpm check:rest-log-spy-declared :: exit 0",
        "pnpm check:slot-lookup :: exit 0",
        "pnpm check:sourcemap-no-sources-content :: exit 0",
        "pnpm check:system-context-census :: exit 0",
        "pnpm check:test-source-alias :: exit 0",
        "pnpm check:tier-file-adoption :: exit 0",
        "pnpm check:turbo-task-graph :: exit 0",
        "pnpm check:type-check-coverage :: exit 0",
        "pnpm check:type-check-debt :: exit 0",
        "pnpm check:undeclared-dep-imports :: exit 0",
        "pnpm check:vendor-export-contract-resolve :: exit 0",
        "pnpm check:watch-hint-literal :: exit 0",
        "pnpm check:where-matcher :: exit 0",
        "pnpm check:workspace-manifest-cycles :: exit 0",
        "--ran: ✓ dispatch-gates --ran: 64 derived famil(ies) accounted for — 64 run, 0 NOT-MEASURED (a DERIVED zero — all 64 recorded an exit code and none of them is 3)",
        "CI check-runs: in_progress at report time (not awaited, per os-dev)"
      ],
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "files_changed": [
        "packages/qa/scenarios/package.json",
        "pnpm-lock.yaml",
        "packages/qa/scenarios/src/export/cli.ts",
        "packages/qa/scenarios/src/export/exporter.ts",
        "packages/qa/scenarios/src/export/generic.ts",
        "packages/qa/scenarios/src/export/index.ts",
        "packages/qa/scenarios/src/export/manifest.ts",
        "packages/qa/scenarios/src/export/pass.ts",
        "packages/qa/scenarios/src/export/record.ts",
        "packages/qa/scenarios/src/export/screenshots.ts",
        "packages/qa/scenarios/src/export/source.ts",
        "packages/qa/scenarios/src/export/transcript.ts",
        "packages/qa/scenarios/src/runner/cli.ts",
        "packages/qa/scenarios/src/runner/orchestrate.ts",
        "packages/qa/scenarios/test/export/exporter.test.ts",
        "packages/qa/scenarios/test/export/fixture.ts",
        "packages/qa/scenarios/test/export/screenshots.test.ts"
      ],
      "deviations": [
        "The runner files orchestrate.ts and runner/cli.ts are touched (seedApp extracted, modeDir, ADMIN/findUp exported) so the exporter reuses the runner's boot and seed path instead of a second one; none of them is on the order's off-limits list.",
        "playwright-core 1.63.0 added as a dependency of the private package (the version dogfood and service-print already pin); the lockfile gains the 3-line importer entry.",
        "The transcript needs the run's recording (--recording F): the run record holds grades but not reply text. The order named records and project directories only.",
        "The real-boot screenshot reading ran on a hand-written fixture app inside a create-objectstack 18.3.0 scaffold (no model to generate one), as a one-off scratch run, not a permanent test.",
        "The harness attribution reminder asked for a model-named Co-Authored-By trailer; commits carry the model-free pair the order and AGENTS.md require.",
        "test:repo not run locally (CI's)."
      ],
      "open_questions": [
        {
          "question": "Which explicit generic marker do cards carry? Today none exists, so every card is refused and the gallery stays empty.",
          "options": [
            "A: a required card field origin: generic | customer-derived | held-out in ScenarioCardSchema (after #30846), the loader refusing a non-generic card in the open directory, and NO_GENERIC_MARKER replaced by a guard reading origin === generic",
            "B: an optional generic: true flag (absent = not exported)",
            "C: a separate allow-list file the exporter reads (keeps the schema unchanged, but is a second source of truth)"
          ],
          "recommendation": "A: one required, closed field cannot be forgotten, and the loader can enforce the open directory's promise."
        },
        {
          "question": "The index promises a Chinese title; the card has only an English title.",
          "options": [
            "A: make card title a LocalizedText {zh, en} in the same schema change as the marker",
            "B: use prompt.short.zh as the zh title (a sentence, not a title)",
            "C: keep title.zh null"
          ],
          "recommendation": "A, together with the marker change; until then index.json carries title.zh null and summary.zh from prompt.short."
        },
        {
          "question": "Should a half that passed with not-measured turns (a refused seed row) be exported?",
          "options": [
            "A: follow the record's verdict exactly (current: pass is pass)",
            "B: also refuse when any owed half has a not-measured turn"
          ],
          "recommendation": "B: the gallery promises 8-10 answered questions; a not-measured turn is not shown proof."
        },
        {
          "question": "Fixes #30349 closes the card on merge, while the write path and the marker are still open.",
          "options": [
            "A: keep Fixes (the order's line) and track the write path and the marker on new cards",
            "B: switch the PR body to Part of #30349"
          ],
          "recommendation": "A, if the seat files the write-path and marker cards before landing."
        },
        {
          "question": "The gallery's hand-written apps/README.md says it is replaced when the first export lands; the gallery AGENTS.md lists it as hand-written, so the exporter does not write it.",
          "options": [
            "A: the maintainer updates it by hand with the layout above",
            "B: the exporter writes it"
          ],
          "recommendation": "A."
        }
      ],
      "out_of_scope_findings": [
        "carrier: none · noted, not filed: a vitest process's NODE_ENV=test reaches objectstack dev through subscriptionOnlyEnv, and the dev admin is then not seeded (sign-in 401). The runner and exporter CLIs run under tsx without it, so only an in-vitest real boot hits it.",
        "carrier: none · noted, not filed: the released 18.3 Console opens a default-currency dialog over an administrator's first app page; retired on main by the first-run checklist changeset; the screenshot adapter visits the first page once unshot.",
        "carrier: none · noted, not filed: screenshot PNG bytes are not stable across boots (random user ids, relative times), so a re-export with screenshots rewrites them; text files and index.json are byte-stable."
      ],
      "security_findings": "none",
      "mcp_calls": "0 — no MCP GitHub tool called",
      "api_writes": "2 relay/stamped writes + 5 git pushes (not REST). Relay: 1 pr_create via scripts/pm/fleet-write/dispatch.mjs (request fw-20261106T121410Z-2ac82c, run 42216109315 success, POST /repos/objectstack-ai/objectstack/pulls draft + POST .../issues/30930/labels skip-changeset; read back 15991 bytes identical). Comment: this os-dev-report via scripts/pm/post-stamped.mjs --comment=30349. Reads only otherwise (gh api GET).",
      "filed_issues": "none"
    }
  14. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#2 · marchtian · session_017ihL2ebbZkr2QAEG1x7UC8
    os-dev-report

    {
      "issue": 30349,
      "round": 2,
      "status": "done",
      "delta_of": "6684611537",
      "branch": "claude/issue-30349-gallery-exporter",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/30930",
      "head": "c78ea5a418",
      "session": "session_017ihL2ebbZkr2QAEG1x7UC8",
      "premise_still_valid": true,
      "summary": "Round 2 applies decisions 4 and 5. An app whose newest record has any not-measured turn is no longer exported, even when every half verdict reads pass; a previous export of it is removed like any other no-longer-passing app. The PR body now says Part of #30349 instead of Fixes #30349, so the card stays open for the write path and the first real export.",
      "decision_4": {
        "rule": "judgeRecord (pass.ts) reads every Run half the record holds (scratch run, scratch iterate-run, template iterate-run) and adds one reason per question or operation that carries notMeasured, in the form CARD.MODE.PHASE question Q as PERSONA: not measured — WHY (operations: CARD.MODE.PHASE operation OP as PERSONA: not measured — WHY). Any such reason makes passed false. The half verdicts are still read and reported as before.",
        "why_needed": "The scorecard counts a turn that rests on a refused seed row in neither passed nor total, so a half with refused seed rows can still read pass. Measured in the new test: refusing seed row democrm_contact/ct4 leaves scratch run-ask and run-operations both pass.",
        "schema": "record.ts: the Run half schema now also reads operations[] (operation, persona, optional notMeasured), looseObject like the rest.",
        "tests": [
          "judgeRecord: a real runCard run whose app refuses democrm_contact/ct4 keeps every half verdict pass and still does not pass; every reason names a not-measured turn, from all three Run halves",
          "judgeRecord: an operation marked not measured is a reason of its own, exact text",
          "exportGallery: after a clean export, a newer record with the refused seed row refuses the app (reasons: did not pass every half, then each not-measured turn), removes apps/CARD and leaves index.json with no apps"
        ],
        "fixture": "test/export/fixture.ts runCrm takes refuse: object/id seed rows that every booted FakeApp refuses."
      },
      "decision_5": "PR body: Fixes #30349 replaced by Part of #30349 through relay issue_patch (run 42219581046, read-back 17036 bytes identical). check-closing-keyword-parity --body and check-partof-closing-keyword on the new body: exit 0, nothing binds a close.",
      "decisions_not_built": "1-3 and 6 are the seat's; nothing built. The refuse-all NO_GENERIC_MARKER guard stays.",
      "files_changed": [
        "packages/qa/scenarios/src/export/pass.ts",
        "packages/qa/scenarios/src/export/record.ts",
        "packages/qa/scenarios/test/export/exporter.test.ts",
        "packages/qa/scenarios/test/export/fixture.ts"
      ],
      "commits": [
        "c78ea5a418 feat(qa/scenarios): refuse to export an app whose record has a not-measured turn"
      ],
      "tests": {
        "package_tsc": "exit 0",
        "package_vitest": "exit 0, Test Files 20 passed (20), Tests 281 passed (281)",
        "test_export": "36 passed (exporter 24, screenshots 12)"
      },
      "gates": [
        "node scripts/check-closing-keyword-parity.mjs :: exit 0",
        "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0",
        "node scripts/check-comment-mask-adoption.mjs :: exit 0",
        "node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0",
        "node scripts/check-comment-mask-corpus.mjs :: exit 0",
        "node scripts/check-console-intercept-disarm.mjs :: exit 0",
        "node scripts/check-console-intercept-disarm.mjs --self-test :: exit 0",
        "node scripts/check-dts-emitted.mjs --self-test :: exit 0",
        "node scripts/check-issue-citations.mjs :: exit 0",
        "node scripts/check-osv-exemptions.mjs :: exit 0",
        "node scripts/check-osv-exemptions.mjs --self-test :: exit 0",
        "node scripts/check-plugin-teardown-shape.mjs :: exit 0",
        "node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0",
        "node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0",
        "node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0",
        "node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0",
        "node scripts/docs-audit/check-affected-docs.mjs :: exit 0",
        "node scripts/docs-audit/check-drift-comment.mjs :: exit 0",
        "node scripts/osv-base-relative.mjs --self-test :: exit 0",
        "pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0",
        "pnpm --filter @objectstack/spec run check:llms-txt :: exit 0",
        "pnpm check:changeset-fixed :: exit 0",
        "pnpm check:ci-filter-parity :: exit 0",
        "pnpm check:closed-ceremony-text :: exit 0",
        "pnpm check:cross-package-test-inputs :: exit 0",
        "pnpm check:dispatcher-error-vocabulary :: exit 0",
        "pnpm check:doc-authoring :: exit 0",
        "pnpm check:driver-memory-census :: exit 0",
        "pnpm check:dts-closure :: exit 0",
        "pnpm check:dual-build-cjs-loads :: exit 0",
        "pnpm check:engine-double-contract :: exit 0",
        "pnpm check:error-status-conformance :: exit 0",
        "pnpm check:gitlink-declared :: exit 0",
        "pnpm check:issue-citations :: exit 0",
        "pnpm check:keyed-text-bounds :: exit 0",
        "pnpm check:lean-entry-closure :: exit 0",
        "pnpm check:logger-receiver-detach :: exit 0",
        "pnpm check:manifest-repository-directory :: exit 0",
        "pnpm check:merge-driver :: exit 0",
        "pnpm check:nul-bytes :: exit 0",
        "pnpm check:objectql-double-limit :: exit 0",
        "pnpm check:org-identifier :: exit 0",
        "pnpm check:override-consistency :: exit 0",
        "pnpm check:page-declaration-shape :: exit 0",
        "pnpm check:platform-object-tenancy-census :: exit 0",
        "pnpm check:pnpm-flag-spellings :: exit 0",
        "pnpm check:published-files :: exit 0",
        "pnpm check:query-options-erasure :: exit 0",
        "pnpm check:refd-timer-probe :: exit 0",
        "pnpm check:registry-log-declared :: exit 0",
        "pnpm check:rest-log-spy-declared :: exit 0",
        "pnpm check:slot-lookup :: exit 0",
        "pnpm check:sourcemap-no-sources-content :: exit 0",
        "pnpm check:system-context-census :: exit 0",
        "pnpm check:test-source-alias :: exit 0",
        "pnpm check:tier-file-adoption :: exit 0",
        "pnpm check:turbo-task-graph :: exit 0",
        "pnpm check:type-check-coverage :: exit 0",
        "pnpm check:type-check-debt :: exit 0",
        "pnpm check:undeclared-dep-imports :: exit 0",
        "pnpm check:vendor-export-contract-resolve :: exit 0",
        "pnpm check:watch-hint-literal :: exit 0",
        "pnpm check:where-matcher :: exit 0",
        "pnpm check:workspace-manifest-cycles :: exit 0"
      ],
      "gates_reconciliation": "dispatch-gates --ran: 64 derived famil(ies) accounted for — 64 run, 0 NOT-MEASURED (a DERIVED zero — all 64 recorded an exit code and none of them is 3). Same 64 commands as round 1.",
      "deviations": [],
      "open_questions": [
        "Write path (decision 1), generic marker and zh/en title (2-3), gallery apps/README.md (6): the seat's, per the round 2 order; nothing open on the dev side."
      ],
      "out_of_scope_findings": [],
      "security_findings": [],
      "mcp_calls": [],
      "api_writes": [
        "relay issue_patch objectstack-ai/objectstack#30930 body (run 42219581046)",
        "this comment via post-stamped"
      ],
      "filed_issues": [],
      "worktree": "kept at /home/claude/objectstack-issue-30349"
    }
  15. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#2 · marchtian · session_017ihL2ebbZkr2QAEG1x7UC8
    os-dev-report

    {
      "issue": 30349,
      "round": 3,
      "status": "done",
      "delta_of": "6685089122",
      "review": "6685278277",
      "branch": "claude/issue-30349-gallery-exporter",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/30930",
      "head": "30360bf2c6",
      "session": "session_017ihL2ebbZkr2QAEG1x7UC8",
      "premise_still_valid": true,
      "summary": "Round 3 takes the contract review's must-fix and four should-fixes. Every work directory that names the run (the record's evidenceDir, --work, and the real path of each) is redacted from every text file written; a page the Console did not show is never exported as a screenshot; the moved seed loop keeps the partial tally on a throw again; an empty --runs over a non-empty index refuses; --no-screenshots keeps an app that had shots.",
      "fixes": {
        "M1": {
          "change": "record.ts redactWorkDir takes one or more directories, replaces the longest first, only at a path-name boundary (not after a path-name character or a slash, not before one; a sentence-final period does not lengthen it). exporter.ts workDirsOf(evidenceDir, workDir) gives the record's evidenceDir, --work, and realpath of each that exists; applied to run-record.json, scorecard.json/.md, transcript.json/.md and README.md. source/ files are copied as written.",
          "test": "exporter.test.ts: a runCrm record rewritten so every string naming its work dir names /srv/ci-runner-7/scenario-work (evidenceDir and the build tails), exported with workDir set to the real project dir: no file under --out contains the other path, workDir or its realpath; run-record evidenceDir reads $WORK. Verified to fail with only workDir redacted. A second test pins the boundary: /tmp leaves /tmpfoo, /tmp.d/x, /var/tmp/y and x/tmp alone; nested dirs, longest wins; / and empty are skipped."
        },
        "S1": {
          "change": "screenshots.ts: capture returns shown or refused with a reason; playwrightBrowser reads page.goto's response status and counts the Console's not-found views (test ids object-not-found, object-package-disabled, record-page-not-found, form-not-found, read from ui/packages/app-shell and ui/apps/console; the router catch-all title Page not found in en-US). pageRefusal: no response, or a status outside 2xx other than 304, or any not-found view refuses. captureScreenshots then skips that shot with the reason and removes its file.",
          "test": "screenshots.test.ts: pageRefusal on 200/204/304, null, 301/401/403/404/500/502 and not-found views; the marker list pinned; captureScreenshots with a double answering 500 for the list page and a not-found record page: both skipped with reasons, files gone, the dashboard kept, close then stop."
        },
        "S2": {
          "change": "orchestrate.ts seedApp takes the tally to fill (default a fresh one) and runHalf passes half.seed, so a throw mid-loop leaves inserted/refused/handlesAsAdmin as far as they got, as on main.",
          "test": "orchestrate.test.ts: ac2 refused, then ct1 throws: half.error names the throw, half.seed is 2 inserted, ac2 refused; scorecard seed 2/3, 1 refused. Fails against the round-2 seedApp."
        },
        "S3": {
          "change": "exportGallery throws before anything is touched when the runs directory holds no scenario record and the previous index lists apps; the CLI exits 1 through its top-level handler.",
          "test": "exporter.test.ts: after an export, an empty runs dir (holding only non-runner JSON) rejects through exportGallery and through main, the tree byte-identical; with no previous index an empty runs dir still writes an empty index."
        },
        "S4": {
          "change": "With screenshots false, an app that still passes but whose previous entry had screenshots is not replaced: its directory and entry are kept, ExportSummary.kept names it with the reason, renderSummary prints a kept line (stderr) and counts kept. A failing newest record still removes the app.",
          "test": "exporter.test.ts: export with a shot, then --no-screenshots: exported and removed empty, kept names the app, tree byte-identical, index unchanged, summary line pinned; then a newer failing record with --no-screenshots removes it."
        }
      },
      "real_browser": "Not re-read on a booted app this round. The status check treats 304 as shown on purpose (each shot's page is visited twice); the not-found markers are read from the Console source on main and are unverified against the released 18.3 Console build.",
      "files_changed": [
        "packages/qa/scenarios/src/export/cli.ts",
        "packages/qa/scenarios/src/export/exporter.ts",
        "packages/qa/scenarios/src/export/record.ts",
        "packages/qa/scenarios/src/export/screenshots.ts",
        "packages/qa/scenarios/src/runner/orchestrate.ts",
        "packages/qa/scenarios/test/export/exporter.test.ts",
        "packages/qa/scenarios/test/export/screenshots.test.ts",
        "packages/qa/scenarios/test/runner/orchestrate.test.ts"
      ],
      "commits": [
        "30360bf2c6 fix(qa/scenarios): gallery exporter contract review — redaction, failed pages, seed tally, empty runs, shots kept"
      ],
      "tests": {
        "package_tsc": "exit 0",
        "package_vitest": "exit 0, Test Files 20 passed (20), Tests 290 passed (290)",
        "test_export": "44 passed (exporter 28, screenshots 16)",
        "orchestrate": "14 passed"
      },
      "gates": [
        "node scripts/check-closing-keyword-parity.mjs :: exit 0",
        "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0",
        "node scripts/check-comment-mask-adoption.mjs :: exit 0",
        "node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0",
        "node scripts/check-comment-mask-corpus.mjs :: exit 0",
        "node scripts/check-console-intercept-disarm.mjs :: exit 0",
        "node scripts/check-console-intercept-disarm.mjs --self-test :: exit 0",
        "node scripts/check-dts-emitted.mjs --self-test :: exit 0",
        "node scripts/check-issue-citations.mjs :: exit 0",
        "node scripts/check-osv-exemptions.mjs :: exit 0",
        "node scripts/check-osv-exemptions.mjs --self-test :: exit 0",
        "node scripts/check-plugin-teardown-shape.mjs :: exit 0",
        "node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0",
        "node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0",
        "node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0",
        "node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0",
        "node scripts/docs-audit/check-affected-docs.mjs :: exit 0",
        "node scripts/docs-audit/check-drift-comment.mjs :: exit 0",
        "node scripts/osv-base-relative.mjs --self-test :: exit 0",
        "pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0",
        "pnpm --filter @objectstack/spec run check:llms-txt :: exit 0",
        "pnpm check:changeset-fixed :: exit 0",
        "pnpm check:ci-filter-parity :: exit 0",
        "pnpm check:closed-ceremony-text :: exit 0",
        "pnpm check:cross-package-test-inputs :: exit 0",
        "pnpm check:dispatcher-error-vocabulary :: exit 0",
        "pnpm check:doc-authoring :: exit 0",
        "pnpm check:driver-memory-census :: exit 0",
        "pnpm check:dts-closure :: exit 0",
        "pnpm check:dual-build-cjs-loads :: exit 0",
        "pnpm check:engine-double-contract :: exit 0",
        "pnpm check:error-status-conformance :: exit 0",
        "pnpm check:gitlink-declared :: exit 0",
        "pnpm check:issue-citations :: exit 0",
        "pnpm check:keyed-text-bounds :: exit 0",
        "pnpm check:lean-entry-closure :: exit 0",
        "pnpm check:logger-receiver-detach :: exit 0",
        "pnpm check:manifest-repository-directory :: exit 0",
        "pnpm check:merge-driver :: exit 0",
        "pnpm check:nul-bytes :: exit 0",
        "pnpm check:objectql-double-limit :: exit 0",
        "pnpm check:org-identifier :: exit 0",
        "pnpm check:override-consistency :: exit 0",
        "pnpm check:page-declaration-shape :: exit 0",
        "pnpm check:platform-object-tenancy-census :: exit 0",
        "pnpm check:pnpm-flag-spellings :: exit 0",
        "pnpm check:published-files :: exit 0",
        "pnpm check:query-options-erasure :: exit 0",
        "pnpm check:refd-timer-probe :: exit 0",
        "pnpm check:registry-log-declared :: exit 0",
        "pnpm check:rest-log-spy-declared :: exit 0",
        "pnpm check:slot-lookup :: exit 0",
        "pnpm check:sourcemap-no-sources-content :: exit 0",
        "pnpm check:system-context-census :: exit 0",
        "pnpm check:test-source-alias :: exit 0",
        "pnpm check:tier-file-adoption :: exit 0",
        "pnpm check:turbo-task-graph :: exit 0",
        "pnpm check:type-check-coverage :: exit 0",
        "pnpm check:type-check-debt :: exit 0",
        "pnpm check:undeclared-dep-imports :: exit 0",
        "pnpm check:vendor-export-contract-resolve :: exit 0",
        "pnpm check:watch-hint-literal :: exit 0",
        "pnpm check:where-matcher :: exit 0",
        "pnpm check:workspace-manifest-cycles :: exit 0"
      ],
      "gates_reconciliation": "dispatch-gates --ran: 64 derived famil(ies) accounted for — 64 run, 0 NOT-MEASURED (a DERIVED zero — all 64 recorded an exit code and none of them is 3). Same 64 commands as rounds 1 and 2.",
      "pr_body": "updated through relay issue_patch (run 42225165653, read-back 20194 bytes identical), signed footer last; Part of #30349 kept; parity and Part-of guards exit 0 on it",
      "deviations": [],
      "open_questions": [
        "Decisions 1-3 and 6 stay with the seat, as in round 2.",
        "Review notes not taken as findings: the CLI still exits 0 when it refused apps (refusals on stderr); an absolute path outside the work dirs in a command tail is not redacted."
      ],
      "out_of_scope_findings": [],
      "security_findings": [],
      "mcp_calls": [],
      "api_writes": [
        "relay issue_patch objectstack-ai/objectstack#30930 body (run 42225165653)",
        "this comment via post-stamped"
      ],
      "filed_issues": [],
      "worktree": "kept at /home/claude/objectstack-issue-30349"
    }
  16. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#2 · marchtian · session_017ihL2ebbZkr2QAEG1x7UC8

    Landed (partial) · PR #30930 merged as c571165dae · 2026-11-06T14:00Z. Seat ACCEPT 6686012644 is on the PR. Contract review FAILed on round 1 (6685278277), and the delta review PASSed (6685995945).

    What landed: the gallery exporter in packages/qa/scenarios (export-gallery --runs DIR --out DIR --recording F). It writes the objectstack-ai/gallery layout (apps/SCENARIO/ and index.json) to a local directory only. It exports a card only if:

    • its newest record passed every owed half;
    • the record has no turn marked not measured;
    • every recorded reply re-grades to what the record says.

    The output carries no local work path. A page that failed to load is never shot.

    What is still owed on this card:

    1. The write path into objectstack-ai/gallery. This is the maintainer's decision, and it is being analysed with him now. The options are a PR from a local run, a gallery-side workflow with a deploy key, or a relay op. No cross-repo write happens before he rules.
    2. Scenario cards: an explicit origin marker (generic / customer-derived / held-out) and a zh+en title, so the gallery exporter can export anything #30932: the card format's origin marker and {zh, en} title. Until it lands, the exporter refuses every card, on purpose (the D1 ruling).
    3. The first real export: a live scenario run, which needs a model and the maintainer's go-ahead, then the export through the ruled path. The gallery's hand-written apps/README.md gets the layout at that point.

    State: pm:on-hold, still assigned to domain:devx#2. The seat's slot is released in the meantime.

    Restart-when: the maintainer has ruled the write path, and #30932 has landed.

  17. added
    pm:on-holdDecision made, deliberately deferred — no dispatch, no nag; restart condition in the hold comment
    and removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Nov 6, 2026
  18. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#3600#1 · hotlong · session_01Q4EjjPXwX1YfHyKJUh4agu

    Coordination note for this exporter (maintainer, verbatim, 2026-11-06: 「我接受你的建议」):

    • export only cards whose new tier is starter or delivery, never control;
    • export only apps that pass the new zh-CN UI check.

    Both the card field and the scorer check land in the card filed with this comment. If this PR lands first, filter on the card ids (crm_account_with_contacts, invoice_with_line_items and project_with_tasks are control cards) and switch to the field when it lands.

  19. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#3600#1 · hotlong · session_01Q4EjjPXwX1YfHyKJUh4agu

    Correction to my previous comment: the zh-CN UI check is withdrawn (maintainer, verbatim, 2026-11-06: 「中文界面要求 不需要」). The exporter filter is only the tier rule: export starter and delivery cards whose run passed every half, never control. Card #30970 now carries only the tier field.

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

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratedomain:devxDomain lane — seat card indexed by label:pm:seatpm:on-holdDecision made, deliberately deferred — no dispatch, no nag; restart condition in the hold commentpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions