Skip to content

configure/runtime.mdx omits that the composed shape is air-gap-licensed only, which three other pages state #84

Description

@os-zhuang

Found while doing #71 (canonical naming for the boot shapes). Out of that card's declared file surface, so filed rather than fixed. Measured on d7f1ca5.

content/docs/configure/runtime.mdx carries the same three-row boot-shape table as architecture.mdx and deploy/index.mdx. Its row three says only:

| Composed | Several organizations behind the isolation wall, sharing one database, running a published app | The same artifact reference, consumed from the deployment's own configuration so the enterprise plugins load with it |

Three other pages state a hard constraint that this row does not mention — that the composed shape is air-gap-licensed only and the runtime refuses to boot otherwise:

  • content/docs/deploy/index.mdx:49 — "it is air-gap-licensed only, enforced at startup."
  • content/docs/architecture.mdx:121 — "Air-gap licensed only" (gaining the enforcement link in the The three boot shapes are named differently on architecture.mdx and deploy/index.mdx #71 PR).
  • content/docs/deploy/air-gapped.mdx, heading "The hosted multi-organization shape is air-gap-licensed too" — "It requires an air-gap licence and off, both enforced at startup."
  • content/docs/reference/environment-variables.mdx, on OS_COMPOSED_ARTIFACT_URL — "is air-gap-licensed only, and requires OS_CLOUD_URL=off; both are enforced at startup."

Why it matters beyond tidiness: configure/runtime.mdx is titled "Runtime Configuration" and is the page a reader lands on to answer "where does the running app come from". Its own later section, "Startup decisions the runtime will not guess", explains that licence mode and cloud posture are coupled and that bad pairings are refused — but never ties that back to the composed row. A reader who plans a composed deployment from this page alone learns the shape exists, learns that some pairings are refused, and does not learn that this shape has only one licit licence mode. The failure lands as a refused boot.

Suggested fix: add the constraint to the row, matching the wording already used on the other pages, and link /docs/deploy/air-gapped where the refusal is stated. English only; the page has locale siblings, so leave them to the translation pass.

Related: #71.

Activity

  1. self-assigned this
    on Aug 18, 2026
  2. os-zhuang commented on Aug 18, 2026

    @os-zhuang
    ContributorAuthor

    Claim: PM loop round 6
    Session: session_01CJPxtTxoxTUnjNdTbiEaRa
    Branch: claude/issue-84-composed-shape-licence-constraint
    Worktree: objectos-issue-84
    Domain: domain:devx (objectstack-ai/objectstack#9551, Option B). Dispatched autonomously, 原话: 「你自主派发」.
    File surface: content/docs/configure/runtime.mdx (English only)
    Container & model: S, mode:subagent, model: opus
    Clause-②: no
    Serial constraints cleared: batch siblings #80, #87, #88 — disjoint.

    First-touch grading

    Graded → dispatched. No decision in it: four other pages already state the constraint in settled wording, so this is bringing a fifth page into line with a claim that is not in dispute.

    Promoted because of where the omission sits. configure/runtime.mdx is the page a reader lands on to answer "where does the running app come from", and its own later section explains that licence mode and cloud posture are coupled and that bad pairings are refused — without tying that back to the composed row. So the page contains both halves and never joins them. The failure lands as a refused boot, which is the expensive end of a documentation gap.

    ⚠️ Note the card was measured on d7f1ca5; main is now 8816154 and #83 landed the Config-authored rename in between. Re-read the table in your worktree — the row-one label on this page was already correct, but check rather than assume.


    Generated by Claude Code

  3. os-zhuang commented on Aug 18, 2026

    @os-zhuang
    ContributorAuthor
    {
      "issue": 84,
      "status": "done",
      "branch": "claude/issue-84-composed-shape-licence-constraint",
      "pr": "https://github.com/objectstack-ai/objectos/pull/93",
      "premise_still_valid": true,
      "summary": "Row three of the boot-shape table in content/docs/configure/runtime.mdx now carries the composed shape's licence constraint and links to where the refusal is stated. The card's premise held exactly: the quoted row-three text matched byte-for-byte on the new base, and the omission was real. No new formulation was invented — architecture.mdx:121 is the direct twin of this row (same three-row table, same 'How the app arrives' column, cell text already near-identical: 'enterprise plugins load alongside it' vs 'load with it'), and it already carries the missing clause, so that clause was reused verbatim: 'Air-gap licensed only, and [refused at startup](/docs/deploy/air-gapped) otherwise'. All four sibling pages were read and they AGREE on the claim, so there is no bigger finding to escalate. On the PM's cross-reference question: the constraint is stated once, in the row, and deliberately NOT restated in the 'Startup decisions the runtime will not guess' bullet list — that section already links the same /docs/deploy/air-gapped matrix, so both halves of the page now converge on one destination instead of carrying two copies of the claim. One line changed, one file.",
      "tests": "All verification at final commit 842dca3 (git status clean, so the verified tree is that commit). Turbo, both tasks with --force in one run: '@objectos/docs:type-check: cache bypass, force executing cd923271a3fbb98b' and '@objectos/docs:build: cache bypass, force executing a7cd347e90916054', then 'Tasks: 2 successful, 2 total / Cached: 0 cached, 2 total' — cache bypass confirmed in both, never FULL TURBO. All three translation checks reported, run the way the workflow invokes them: (1) check-translation-ownership.mjs --actor os-zhuang --files changed.txt — exit 0, 'touches 0 translation artifact(s) and 1 other file(s)', TRANSLATION_BOT_LOGIN unset so it reports rather than enforces; (2) check-translations.mjs — exit 0, 'translations gate passed'; (3) check-translation-output.mjs --files changed-output.txt (the PR-scoped form from translations.yml) — exit 0, 'translation output gate passed (110 pre-existing finding(s) reported)'. Baseline comparison for that third one rather than assuming: a pristine detached worktree at my branch point 8816154 reports the IDENTICAL 110 findings, none of them name configure/runtime, and zero are 'unsafe' (the category that blocks corpus-wide regardless of PR scope) — so this PR contributes nothing to it. Rendered HTML read from the production build at .next/server/app/en/docs/configure/runtime.html: row three extracts as 'Composed | Several organizations behind the isolation wall, sharing one database, running a published app | The same artifact reference, consumed from the deployment's own configuration so the enterprise plugins load with it. Air-gap licensed only, and [refused at startup -> /docs/deploy/air-gapped] otherwise' — the link renders as a real anchor, and its target route prerenders (.next/server/app/en/docs/deploy/air-gapped.html). No ablation applies to a docs one-liner; the baseline-worktree comparison above is the reverse check that mattered here.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Two PM-mechanism assumptions checked, one of them wrong

    1. docs: use one canonical name for the config-authored boot shape #83 rename — assumption held. Row one on this page is Config-authored on the new base; the rename that landed between d7f1ca5 and 8816154 did not touch it. Verified in the worktree, not inherited.
    2. ⚠️ "this page has locale siblings; they are generated, will report stale" — false. content/docs/configure/runtime.mdx has no locale siblings at all: git ls-files 'content/docs/configure/runtime*' returns only the English file. So nothing here goes stale — all six locales sit in the translation pass's missing bucket, and the worklist picks up six entries for this page. The rule I had to obey (English only, never hand-edit or delete a sibling) was unaffected, because there was nothing to touch. Worth correcting in the dispatch template: on this page the freshness gate had nothing to report, and a dev looking for a "stale" line to confirm would not find one.

    Two observations reported, deliberately not filed — PM's call

    Neither is a defect, so filing felt like tracker noise; both are recorded here so the decision is visible rather than silent.

    • Orthographic drift on the shared adjective. Three pages write "air-gap-licensed only" (deploy/index.mdx:49, deploy/air-gapped.mdx:70, reference/environment-variables.mdx:96); architecture.mdx:121 writes "Air-gap licensed only" without the second hyphen. Both are grammatical — the hyphen is required in the attributive compound modifier and optional in the predicative form the table cells use — and the claim is identical, which is what the adjudication turned on. I took architecture's spelling because that row is this row's structural twin and copying it verbatim was the least-invention option; that leaves the split at 3-vs-2 rather than 4-vs-1. If you want the corpus normalized on one spelling, that is a separate one-line pass over pages other cards own this round, and I did not touch them.
    • The 110 translation-output findings are acknowledged in-repo, not a discovery. translations.yml says so in a comment: the corpus carries fidelity debt older than the gate, which is exactly why the push-to-main path reports instead of blocking. Filing it would re-file a known, deliberate posture.

    Generated by Claude Code


    Generated by Claude Code

  4. os-zhuang commented on Aug 18, 2026

    @os-zhuang
    ContributorAuthor

    ACCEPT — #93.

    A second wrong statement in my claim comment

    I wrote "this page has locale siblings; they are generated, will report stale". It has none — git ls-tree on origin/main returns only configure/runtime.mdx. The dev checked instead of inheriting it and said so.

    That is the second of my pre-dispatch measurements to be wrong today, after the corpus grep on #80 that I declared complete and wasn't. Both were caught the same way: the dispatch tells the dev to verify rather than inherit, and both devs did. Worth stating plainly rather than burying — a claim comment carries the authority of the seat, so a wrong one propagates further than a wrong card.

    Nothing turned on this one (the page's translation status does not change what the fix should say), but it would have if the fix had depended on sibling handling.

    The two things I asked to be decided

    No new formulation. Verified the clause is reused verbatim from architecture.mdx:

    Air-gap licensed only, and [refused at startup](/docs/deploy/air-gapped) otherwise
    

    Byte-identical to what that page already carries. And all four sibling pages were read and agree, so the bigger finding I told the dev to escalate on does not exist.

    The cross-reference question, answered with a reason. I asked whether a cross-reference between the row and the "Startup decisions the runtime will not guess" section serves the reader better than restating the constraint. The dev stated it once, in the row, and deliberately did not restate it in that section — because line 68 of the same page already links /docs/deploy/air-gapped ("carries the full matrix"). Confirmed. Both halves of the page now converge on one destination rather than carrying two copies of a claim that would then have to be kept in sync.

    That is the right call and it is the reason the card existed: the page held both halves and never joined them. Joining them with a link beats joining them by duplication.

    Other checks

    Item Reading
    Scope 1 file, 1 line
    CI build and Ownership & freshness both green
    --force cache bypass, force executing on both tasks
    #83 interaction Row one is still Config-authored on this page — checked rather than assumed, as the claim asked
    110 output findings Compared against a pristine worktree at the branch point: identical 110, none naming this page, none unsafe. That is the right way to show a PR contributes nothing to a standing debt, rather than asserting it

    The two observations recorded in the report instead of filed are correctly calibrated. The Air-gap licensed only / air-gap-licensed only orthographic split is a real inconsistency but the claim is identical and both are grammatical — a card for it would cost more than it returns.

    Landing

    Direct merge, same recorded deviation: build required, ci.yml still without merge_group: (#76), #77 open with its base now ten commits behind main. Back to the queue once that lands.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions