@bongos/core 1.19.715 → 1.19.725
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bongos-core.json +491 -421
- package/.claude/skills/backlog-review/SKILL.md +3 -3
- package/.claude/skills/blocker-review/SKILL.md +1 -1
- package/.claude/skills/blocker-solve/SKILL.md +2 -2
- package/.claude/skills/builder-reauth/SKILL.md +1 -1
- package/.claude/skills/builder-redteam/SKILL.md +1 -1
- package/.claude/skills/builder-ship/SKILL.md +1 -1
- package/.claude/skills/builder-start/SKILL.md +1 -1
- package/.claude/skills/design/SKILL.md +2 -2
- package/.claude/skills/feedback/SKILL.md +1 -1
- package/.claude/skills/goal-create/SKILL.md +1 -1
- package/.claude/skills/goal-review/SKILL.md +2 -2
- package/.claude/skills/idea-triage/SKILL.md +5 -5
- package/.claude/skills/merge-mode/SKILL.md +4 -4
- package/.claude/skills/new-project/SKILL.md +17 -17
- package/.claude/skills/planning-session/SKILL.md +11 -11
- package/.claude/skills/recall/SKILL.md +2 -2
- package/.claude/skills/strand-fix/SKILL.md +1 -1
- package/.devcontainer/README.md +2 -2
- package/CONTRIBUTING.md +2 -2
- package/GOVERNANCE.md +4 -4
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +4 -0
- package/clients/bongos-client/index.cjs +4 -0
- package/clients/bongos-client/index.d.ts +5 -0
- package/clients/bongos-client/index.mjs +4 -0
- package/config/branding.neutral.json +4 -2
- package/docs/adr/0001-stack-choice-single-vps.md +2 -2
- package/docs/adr/0002-digitalocean-over-hetzner.md +1 -1
- package/docs/adr/0008-google-chat-oauth-user-auth.md +1 -1
- package/docs/adr/0009-autotile-architecture.md +1 -1
- package/docs/adr/0011-terrain-id-rendering-model.md +3 -3
- package/docs/adr/0013-paths-as-object-overlays-with-autotile-dispatch.md +1 -1
- package/docs/adr/0014-families-canonical-over-vocabulary.md +1 -1
- package/docs/adr/0015-task-dependencies-and-auto-promotion.md +1 -1
- package/docs/adr/0016-trust-boundary-server-enforced-permissions.md +1 -1
- package/docs/adr/0018-three-rank-model-goes-live.md +3 -3
- package/docs/adr/0020-security-model-public-stub.md +9 -9
- package/docs/adr/0021-per-builder-skill-model-preferences.md +2 -2
- package/docs/adr/0022-secrets-policy.md +5 -5
- package/docs/adr/0024-cloneable-repo-local-first-memory.md +3 -3
- package/docs/adr/0024-multi-agent-system-architecture.md +10 -10
- package/docs/adr/0025-offsite-backup-vendor-digitalocean-spaces.md +1 -1
- package/docs/adr/0025-structured-criterion-task-link.md +1 -1
- package/docs/adr/0026-bfg-memory-hygiene-and-cross-builder-learning.md +13 -13
- package/docs/adr/0027-bfg-session-inefficiency-evaluator.md +14 -14
- package/docs/adr/0029-status-page-outage-resilience.md +1 -1
- package/docs/adr/0030-builders-hall-live-updates.md +1 -1
- package/docs/adr/0031-cloud-dev-environments-for-builders.md +7 -7
- package/docs/adr/0032-discord-mirror-of-gds.md +3 -3
- package/docs/adr/0033-discord-bot-service-principal.md +4 -4
- package/docs/adr/0034-thetes-graduated-newcomer-rank.md +1 -1
- package/docs/adr/0035-builder-onboarding-three-paths.md +4 -4
- package/docs/adr/0036-builders-subdomain-and-ui-reorg.md +1 -1
- package/docs/adr/0037-discord-channel-management.md +1 -1
- package/docs/adr/0038-chromebook-ttyd-cloudflare-tunnel.md +5 -5
- package/docs/adr/0039-setup-first-onboarding-ux.md +6 -6
- package/docs/adr/0040-remote-control-default-browser-onramp.md +3 -3
- package/docs/adr/0041-temporary-grader-bypass-killswitch.md +2 -2
- package/docs/adr/0042-builder-self-deploy-ci-auto-merge.md +5 -5
- package/docs/adr/0043-git-ssh-trust-boundary-and-rank-floor-on-permission-paths.md +2 -2
- package/docs/adr/0044-mediterranean-palette-replacement.md +1 -1
- package/docs/adr/0044-per-box-live-game-preview.md +1 -1
- package/docs/adr/0046-sandbox-first-review-gate.md +1 -1
- package/docs/adr/0047-discord-bugs-intake-anti-abuse.md +1 -1
- package/docs/adr/0049-split-parallel-safety-contract.md +1 -1
- package/docs/adr/0050-device-flow-admission-invite-gated-by-default.md +2 -2
- package/docs/adr/0051-full-session-transcript-corpus.md +2 -2
- package/docs/adr/0052-sandbox-for-everyone-game-only-preview.md +1 -1
- package/docs/adr/0053-scoped-dev-box-session.md +1 -1
- package/docs/adr/0054-session-token-reward-cost-plus.md +7 -7
- package/docs/adr/0055-server-mediated-branch-publish.md +2 -2
- package/docs/adr/0056-prod-deploy-script-mirror-and-healthcheck-retry.md +3 -3
- package/docs/adr/0057-container-cost-ledger.md +1 -1
- package/docs/adr/0057-discord-archon-approval-channels.md +2 -2
- package/docs/adr/0058-automatic-gate-surface-gatekeeper.md +6 -6
- package/docs/adr/0059-single-approval-remove-devbox-approval-gate.md +11 -11
- package/docs/adr/0060-gds-retrieval-layer.md +3 -3
- package/docs/adr/0061-context-layer-decomposition.md +2 -2
- package/docs/adr/0062-medusa-instance-model-decoupling.md +5 -5
- package/docs/adr/0063-generated-repo-map-symbol-skeleton.md +2 -2
- package/docs/adr/0064-rename-medusa-to-cloud-bongos.md +3 -3
- package/docs/adr/0065-agpl-nonprofit-ai-first-governance.md +5 -5
- package/docs/adr/0066-generated-file-map-sections.md +2 -2
- package/docs/adr/0067-session-start-fast-forward-pull.md +1 -1
- package/docs/adr/0068-shared-art-key-for-newcomers.md +1 -1
- package/docs/adr/0069-hall-live-channel-sse-decouple.md +3 -3
- package/docs/adr/0070-deterministic-session-card-delivery.md +1 -1
- package/docs/adr/0071-box-confirm-before-destroyed-and-drift-reconcile.md +2 -2
- package/docs/adr/0072-bongos-app-mac-signed-first-windows-deferred.md +2 -2
- package/docs/adr/0072-dev-box-code-staleness-visibility.md +2 -2
- package/docs/adr/0073-builder-needs-signal-and-byok-gemini-key.md +3 -3
- package/docs/adr/0073-secrets-scan-exclude-uri-detector.md +2 -2
- package/docs/adr/0076-in-session-deterministic-compaction.md +2 -2
- package/docs/adr/0077-content-addressed-llm-cache.md +2 -2
- package/docs/adr/0078-deterministic-first-cron.md +3 -3
- package/docs/adr/0079-value-gated-autonomous-engagement.md +2 -2
- package/docs/adr/0080-llm-cache-api-transport-and-trust-model.md +3 -3
- package/docs/adr/0081-tool-agnostic-design-layer.md +3 -3
- package/docs/adr/0082-server-side-merge-conflict-auto-resolution.md +2 -2
- package/docs/adr/0083-modular-architecture-strangler-build-plan.md +3 -3
- package/docs/adr/0085-remove-default-per-builder-spend-cap.md +2 -2
- package/docs/adr/0086-goal-scoped-work-hierarchy.md +15 -15
- package/docs/adr/0087-bongos-app-architecture-and-handoff-contract.md +5 -5
- package/docs/adr/0087-compete-on-governance-not-tooling-cursor.md +1 -1
- package/docs/adr/0088-consistent-character-generative-animation.md +5 -5
- package/docs/adr/0089-modular-multi-model-adversarial-grader.md +15 -15
- package/docs/adr/0090-metic-task-authoring.md +8 -8
- package/docs/adr/0091-bounding-the-kernel-and-db-carve.md +3 -3
- package/docs/adr/0092-hosting-module-and-on-prem-target.md +11 -11
- package/docs/adr/0093-tranche-2-core-carve-sequence.md +5 -5
- package/docs/adr/0094-repo-atlas-self-drawing-codebase-map.md +1 -1
- package/docs/adr/0095-borrowed-memory-and-retrieval-concepts.md +6 -6
- package/docs/adr/0095-cross-agent-context-management.md +5 -5
- package/docs/adr/0096-require-reward-before-workable.md +5 -5
- package/docs/adr/0097-one-active-claim-per-session-and-worktree-binding.md +2 -2
- package/docs/adr/0097-retroactive-reward-backfill.md +4 -4
- package/docs/adr/0098-public-mirror-publish-manifest.md +3 -3
- package/docs/adr/0099-delayed-redacted-mirror-export.md +1 -1
- package/docs/adr/0100-two-repo-instance-model.md +6 -6
- package/docs/adr/0101-goal-hierarchy-phase-5-judge-membership-reward.md +9 -9
- package/docs/adr/0102-finish-bounding-the-kernel.md +1 -1
- package/docs/adr/0103-core-first-extraction-cloud-bongos-trunk.md +6 -6
- package/docs/adr/0103-gdsv4-bongos-consolidation.md +3 -3
- package/docs/adr/0104-trust-gds-api-channel-in-auto-mode.md +3 -3
- package/docs/adr/0105-instance-seed-migrations-out-of-core.md +2 -2
- package/docs/adr/0106-goal-owner-manager-authority.md +2 -2
- package/docs/adr/0107-module-upstreaming-contribution-to-core-catalog.md +6 -6
- package/docs/adr/0108-instance-composition-configurable-root.md +6 -6
- package/docs/adr/0109-self-describing-openapi-and-hosted-docs.md +3 -3
- package/docs/adr/0110-untrack-whole-file-generated-nav-docs.md +1 -1
- package/docs/adr/0111-instance-hosting-provisioning-module.md +18 -18
- package/docs/adr/0112-private-goals-invitations-join-requests.md +7 -7
- package/docs/adr/0113-sandbox-studio-descope-superseded-by-0044-0052.md +1 -1
- package/docs/adr/0114-instance-docs-discovery-contract.md +4 -4
- package/docs/adr/0115-scheduled-routines-portability.md +1 -1
- package/docs/adr/0117-api-versioning-and-deprecation-policy.md +1 -1
- package/docs/adr/0118-api-contract-source-of-truth-code-first.md +1 -1
- package/docs/adr/0120-pay-on-land-and-builder-owned-rebase-gate.md +2 -2
- package/docs/adr/0121-greenfield-vs-brownfield-onboarding-adopt-existing-repo.md +1 -1
- package/docs/adr/0122-onboarding-diagram-renders-deploy-rendered-off-git.md +3 -3
- package/docs/adr/0123-box-idle-sweep-autosave-before-destroy.md +1 -1
- package/docs/adr/0124-typed-api-response-bodies.md +2 -2
- package/docs/adr/0125-control-plane-scaffolds-standalone-instance-repo.md +2 -2
- package/docs/adr/0126-dedicated-cloudbongos-control-plane-droplet.md +3 -3
- package/docs/adr/0127-cross-box-fleet-observability.md +1 -1
- package/docs/adr/0128-provisioning-runner-privilege-and-db-auth-model.md +3 -3
- package/docs/adr/0129-cloudbongos-control-plane-pull-deploy.md +4 -4
- package/docs/adr/0130-provision-remote-exec-over-ssh.md +4 -4
- package/docs/adr/0131-rank-scoped-skill-visibility.md +2 -2
- package/docs/adr/0132-cohost-fleet-control-plane-migration.md +2 -2
- package/docs/adr/0133-github-app-manifest-onboarding-signin.md +1 -1
- package/docs/adr/0134-private-first-npm-distribution.md +3 -3
- package/docs/adr/0135-module-upstream-submission-interim-queue.md +1 -1
- package/docs/adr/0136-update-channel-subscription-policy.md +2 -2
- package/docs/adr/0137-upstream-publish-policy-manual-owner-gated.md +6 -6
- package/docs/adr/0138-bundled-module-deps-declared-per-module.md +4 -4
- package/docs/adr/0142-project-context-is-prose-only.md +2 -2
- package/docs/adr/0143-onboarding-repo-picker-oauth-scope.md +2 -2
- package/docs/adr/0144-devbox-rehome-onto-cloudbongos-plane.md +2 -2
- package/docs/adr/0145-free-hosted-project-tier-isolation-and-domain-separation.md +2 -2
- package/docs/adr/0146-configurable-reward-mode-cost-plus-only.md +4 -4
- package/docs/adr/0147-standalone-live-docs-regen.md +1 -1
- package/docs/adr/0149-bongos-dev-local-launcher.md +1 -1
- package/docs/adr/0150-box-first-boot-bringup-vendored-instances.md +3 -3
- package/docs/adr/0151-governance-permissions-as-atom-ranks-as-roles.md +20 -20
- package/docs/adr/0152-landing-gate-served-by-the-core-app.md +2 -2
- package/docs/adr/0152-metic-task-abandonment.md +7 -7
- package/docs/adr/0153-sequential-chain-runner.md +1 -1
- package/docs/adr/0154-goal-owner-criterion-authoring.md +7 -7
- package/docs/adr/0155-adopt-private-repo-widen-oauth-scope.md +2 -2
- package/docs/adr/0156-user-set-display-name-wins.md +1 -1
- package/docs/adr/0157-archon-is-rank-and-identity-only.md +10 -10
- package/docs/adr/0158-choke-point-verdict-taxonomy.md +1 -1
- package/docs/adr/0159-merge-gate-reconciliation.md +1 -1
- package/docs/adr/0160-main-audit-repo-verifiable-caller.md +1 -1
- package/docs/adr/0161-publish-on-merge.md +2 -2
- package/docs/adr/0163-legacy-display-name-resolution.md +1 -1
- package/docs/adr/0165-instances-hookless-by-default.md +1 -1
- package/docs/adr/0166-module-maintenance-and-deprecation-policy.md +3 -3
- package/docs/adr/0167-gate-trust-link-agpl-default.md +2 -2
- package/docs/adr/0167-module-catalog-source-vs-provenance.md +3 -3
- package/docs/adr/0169-bongos-serve-server-bring-up.md +1 -1
- package/docs/adr/0171-profile-rollup-join-and-consent.md +1 -1
- package/docs/adr/0172-editable-rank-roles-substrate-axis.md +2 -2
- package/docs/adr/0172-per-craft-compensation-ideator-credit-lane.md +4 -4
- package/docs/adr/0173-legacy-multi-ship-shortfall-not-backfilled.md +1 -1
- package/docs/adr/0174-government-terminology-ranks-not-roles.md +1 -1
- package/docs/adr/0177-goal-membership-kind.md +3 -3
- package/docs/adr/0178-the-copy-desk-flag-and-queue.md +1 -1
- package/docs/adr/0179-board-membership-predicate-grammar.md +1 -1
- package/docs/adr/0181-teardown-state-gate-and-address-retention.md +1 -1
- package/docs/adr/0182-project-visibility-two-axes.md +1 -1
- package/docs/adr/0183-criteria-close-themselves.md +1 -1
- package/docs/adr/0184-ship-requires-an-assurance.md +1 -1
- package/docs/adr/0185-spark-handoff-credit-split.md +1 -1
- package/docs/adr/0186-per-project-idea-templates.md +1 -1
- package/docs/adr/0187-collab-four-decisions.md +1 -1
- package/docs/adr/0187-oauth-handshake-cookie-lifetime.md +1 -1
- package/docs/adr/0188-patrol-runs-on-ci-not-the-server.md +2 -2
- package/docs/adr/0189-baseline-floors-and-the-freeze-detector.md +2 -2
- package/docs/adr/0191-majority-and-the-author-who-may-not-decide-alone.md +1 -1
- package/docs/adr/0192-a-category-orients-and-authorises-nothing.md +2 -2
- package/docs/adr/0193-pause-task-scoped-box-slices.md +1 -1
- package/docs/adr/0195-adr-numbers-are-checked-like-migration-numbers.md +2 -2
- package/docs/adr/0196-chrome-world-token-contract-and-the-landing-sheets.md +2 -2
- package/docs/adr/0197-the-ui-design-module.md +1 -1
- package/docs/adr/0198-third-party-skill-vendoring-policy.md +1 -1
- package/docs/adr/0203-doorway-changelog-out-of-module-api.md +2 -2
- package/docs/adr/0204-the-apex-is-dark-only.md +2 -2
- package/docs/adr/0205-federated-checkin-needs-the-hubs-own-signin-witness.md +1 -1
- package/docs/adr/0206-own-scoped-session-revocation-no-absolute-ceiling.md +1 -1
- package/docs/adr/0207-connections-implementation-departures-and-honest-limits.md +1 -1
- package/docs/adr/0208-application-lifecycle-the-handle-waits-and-the-vouch-has-no-minter.md +2 -2
- package/docs/adr/0209-one-budget-for-the-account-existence-oracle.md +1 -1
- package/docs/adr/0210-scouting-graduates-on-an-instance-asserted-owner-claim.md +1 -1
- package/docs/adr/0212-connections-on-the-profile-surface-and-addressing.md +1 -1
- package/docs/adr/0213-platform-terms-acceptance-is-the-hubs-own-witness.md +2 -2
- package/docs/adr/0215-the-recruiter-sliver-is-a-ceiling-not-a-step.md +6 -6
- package/docs/adr/0216-a-fake-pool-interpreter-must-fail-loudly-not-silently-ignore.md +2 -2
- package/docs/adr/0217-rank-gates-inviting-not-viewing.md +1 -1
- package/docs/adr/0218-the-apex-root-web-surface.md +2 -2
- package/docs/adr/0219-a-look-is-a-branding-pack-the-style-library.md +1 -1
- package/docs/adr/0220-an-in-house-rebuilt-skill-is-a-first-party-skill.md +1 -1
- package/docs/adr/0221-the-impeccable-method-is-one-skill-with-sub-command-playbooks.md +2 -2
- package/docs/adr/0222-the-image-family-under-the-hero-only-rule.md +2 -2
- package/docs/adr/0223-the-detector-is-two-tiers-in-the-kit.md +3 -3
- package/docs/adr/0224-module-skills-reach-the-core-checkout-as-untracked-self-ignored-copies.md +1 -1
- package/docs/adr/0225-privacy-suppresses-cross-project-surfaces-only.md +1 -1
- package/docs/adr/0226-one-stale-timer-reporting-degrades-enforcement-refuses.md +1 -1
- package/docs/adr/0227-a-task-network-map-is-refused-the-goal-graph-is-the-map.md +1 -1
- package/docs/adr/0230-a-look-skill-is-a-composition-grammar-the-palette-is-the-pack.md +1 -1
- package/docs/adr/0231-the-style-session-an-owner-authors-a-look-inside-the-contract.md +2 -2
- package/docs/adr/0232-rot-is-derived-not-swept-and-water-is-the-only-new-verb.md +2 -2
- package/docs/adr/0233-a-copy-proposal-is-a-task-carrying-a-patch.md +2 -2
- package/docs/adr/0234-idea-routing-capture-time-promotion-landing-matrix-homeless-inbox.md +2 -2
- package/docs/adr/0235-a-fix-category-is-kindbug-under-a-goal-no-column-required.md +11 -11
- package/docs/adr/0236-many-ideas-one-task-the-lineage-key-is-the-pair.md +5 -5
- package/docs/adr/0237-a-starter-bundle-is-a-preset-over-the-always-on-core-keyed-by-declared-type.md +5 -5
- package/docs/adr/0238-the-join-door-is-a-provisioning-settings-policy-key.md +1 -1
- package/docs/adr/0239-a-recommendation-is-a-table-that-carries-its-own-reason.md +4 -4
- package/docs/adr/0240-a-skipped-module-picker-stores-nothing-and-resolves-the-bundle-on-read.md +4 -4
- package/docs/adr/0241-the-artist-gate-is-a-per-project-deploy-gate-that-reads-a-state.md +1 -1
- package/docs/adr/0242-a-cascade-is-a-declaration-table-on-the-event-that-already-exists.md +2 -2
- package/docs/adr/0243-a-bundle-adjustment-belongs-to-the-bundle-not-the-advice-about-it.md +4 -4
- package/docs/adr/0244-a-catalog-search-is-a-view-over-the-rows-never-an-edit-to-the-selection.md +3 -3
- package/docs/adr/0245-the-way-back-into-an-optional-step-is-that-step-s-own-answer-route.md +7 -7
- package/docs/adr/0246-catalog-membership-is-a-projection-not-a-federation-side-effect.md +1 -1
- package/docs/adr/0247-the-join-door-is-one-composed-answer-and-dark-matter-has-no-live-verdict-yet.md +2 -2
- package/docs/adr/0248-a-hub-project-s-owner-is-the-provisioning-row-carried-on-a-port.md +3 -3
- package/docs/adr/0249-the-invite-step-is-the-done-panel-s-first-act-not-a-rail-step.md +3 -3
- package/docs/adr/0250-strict-versioning-the-version-boundary-is-the-scope-gate.md +6 -6
- package/docs/adr/0251-a-suggestion-of-who-to-invite-is-a-recruiting-surface-and-owes-the-opt-out.md +5 -5
- package/docs/adr/0252-the-self-hosted-visibility-report-is-a-pull-and-auto-appear-buys-a-retraction-lever.md +19 -19
- package/docs/adr/0253-the-takedown-lever-has-two-writers-and-provenance-is-a-column.md +2 -2
- package/docs/adr/0254-a-relayed-link-is-contained-downward-or-it-is-not-published.md +6 -6
- package/docs/adr/0255-a-public-lists-ordering-is-part-of-its-payload.md +2 -2
- package/docs/adr/0256-the-visibility-ceiling-is-the-sign-in-gate-too-and-the-reader-moves-to-the-composer.md +3 -3
- package/docs/adr/0257-auth-resolves-before-the-hall-mounts-anything.md +1 -1
- package/docs/adr/0258-the-public-cli-is-a-generated-client-package-not-the-published-core.md +1 -1
- package/docs/adr/0259-a-projects-departure-from-the-public-list-is-public.md +7 -7
- package/docs/adr/0260-the-application-is-the-consent-and-the-echo-is-the-gate.md +4 -4
- package/docs/adr/0261-a-preselect-always-carries-a-reason-the-bundle-summary-is-the-floor.md +1 -1
- package/docs/adr/0262-a-bug-never-lands-in-the-inbox.md +1 -1
- package/docs/adr/0263-how-a-version-closes.md +3 -3
- package/docs/adr/0264-the-ten-working-areas.md +3 -3
- package/docs/adr/0265-work-with-no-goal-and-the-order-the-cut-needs.md +5 -5
- package/docs/adr/0266-the-board-room-is-its-own-surface-reachable-by-whoever-may-vote.md +2 -2
- package/docs/adr/0267-unanimity-and-the-revise-and-re-sit-loop.md +1 -1
- package/docs/adr/0268-the-constitution-comes-from-two-roots.md +3 -3
- package/docs/adr/0269-the-cli-session-store-is-host-keyed-at-a-fixed-anchor.md +1 -1
- package/docs/adr/0270-rank-floor-derives-without-a-prediction.md +2 -2
- package/docs/adr/0271-the-ideator-surface-is-two-halves.md +3 -3
- package/docs/adr/0272-a-craft-is-an-identity-not-a-toolchain.md +4 -4
- package/docs/adr/0273-hall-nav-can-gate-on-a-permission.md +1 -1
- package/docs/adr/0274-one-kernel-three-role-packs.md +1 -1
- package/docs/adr/0275-one-source-for-a-written-role-responsibility.md +1 -1
- package/docs/adr/0276-the-skill-listing-budget-cannot-hold-every-trigger.md +1 -1
- package/docs/adr/0277-a-box-is-in-use-only-while-a-human-is-attached.md +1 -1
- package/docs/adr/0278-a-gated-project-still-takes-applications.md +2 -2
- package/docs/adr/0279-an-upgrade-is-proven-by-the-served-version-not-the-health-check.md +1 -1
- package/docs/adr/0280-the-unattended-lane-may-drive-a-co-tenant-because-the-roster-is-the-decision.md +1 -1
- package/docs/adr/0281-an-instance-identity-is-its-own-unix-account-and-pg-role.md +2 -2
- package/docs/adr/README.md +294 -294
- package/docs/api/openapi.json +72 -5
- package/docs/api-reference.md +8 -2
- package/docs/architecture.md +22 -22
- package/docs/branding-contract.md +5 -5
- package/docs/canonical-permissions.md +22 -22
- package/docs/design/hall-direction-v2.md +3 -3
- package/docs/design/landing-page-direction-v2.md +1 -1
- package/docs/design/projects-hub-direction-v2.md +1 -1
- package/docs/file-map.md +3 -1
- package/docs/module-api-changelog.md +36 -1
- package/docs/modules-contract.md +25 -25
- package/docs/onboarding/browser-terminal-guide.md +1 -1
- package/docs/onboarding/diagrams/README.md +3 -3
- package/docs/onboarding/drachmae-and-karma.md +13 -13
- package/docs/onboarding/primer.md +7 -7
- package/docs/packs/artist.md +1 -1
- package/docs/packs/ideator.md +1 -1
- package/docs/project-context.template.md +3 -3
- package/docs/recipes/builder-box-lifecycle.md +5 -5
- package/docs/recipes/claude-design-loop.md +1 -1
- package/docs/recipes/core-release-pipeline.md +55 -14
- package/docs/recipes/figma-design-loop.md +1 -1
- package/docs/recipes/gds-db-backup.md +2 -2
- package/docs/recipes/instance-deploy.md +2 -2
- package/docs/recipes/instance-service-restart.md +3 -3
- package/docs/recipes/local-dev.md +2 -2
- package/docs/recipes/managed-settings-remote-control.md +3 -3
- package/docs/recipes/memory-backup.md +4 -4
- package/docs/recipes/memory-sync-conflicts.md +2 -2
- package/docs/recipes/multi-builder-merge.md +2 -2
- package/docs/recipes/ops-gotchas.md +4 -4
- package/docs/recipes/overnight-builder.md +1 -1
- package/docs/recipes/packaging-the-core.md +6 -6
- package/docs/recipes/private-npm-distribution.md +5 -5
- package/docs/recipes/search-index-ingestion.md +3 -3
- package/docs/recipes/self-host.md +5 -5
- package/docs/recipes/server-bring-up.md +1 -1
- package/docs/recipes/ship-and-regrade.md +2 -2
- package/docs/recipes/standalone-live-docs.md +1 -1
- package/docs/recipes/ui-look-before-you-ship.md +1 -1
- package/docs/recipes/upgrading-the-core.md +2 -2
- package/docs/recipes/work-on-a-task-from-your-dev-box.md +6 -6
- package/docs/research/cursor-competitive-analysis.md +3 -3
- package/docs/research/llm-model-agnostic-and-cheaper-grader.md +7 -7
- package/migrations/core_240_artist_gate_indexes.sql +48 -0
- package/migrations/core_240_goal_members_drop_legacy_role.sql +79 -0
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +4 -0
- package/modules/lifecycle/cascade.js +37 -9
- package/modules/lifecycle/db-claims.js +5 -2
- package/modules/lifecycle/db-goals.js +4 -3
- package/modules/lifecycle/db-overrides.js +117 -4
- package/modules/lifecycle/db-tasks.js +63 -0
- package/modules/lifecycle/migrations/lifecycle_001_goal_first_leads.sql +12 -4
- package/modules/lifecycle/module.json +2 -1
- package/modules/lifecycle/pg-constraint-fail.js +49 -0
- package/modules/lifecycle/routes/artist-gate.js +101 -0
- package/modules/lifecycle/routes/claims.js +13 -1
- package/modules/lifecycle/routes/lifecycle.js +6 -0
- package/modules/lifecycle/routes/task-write-routes.js +7 -0
- package/modules/lifecycle/routes/tasks.js +24 -0
- package/modules/lifecycle/task-classifier.js +10 -1
- package/modules/lifecycle/tests/cascade.mjs +43 -0
- package/modules/onboarding/approval-broadcast.js +27 -0
- package/modules/provisioning/provisioning.js +79 -3
- package/modules/provisioning/routes/provisioning.js +6 -1
- package/modules/provisioning/tests/provisioning.mjs +8 -8
- package/modules/security/secret-scrub.js +40 -4
- package/modules/status-ui/public/README.md +1 -1
- package/modules/ui-design/docs/design-contract.md +2 -2
- package/modules/ui-design/kit/README.md +1 -1
- package/modules/ui-design/kit/serve.js +1 -0
- package/modules/ui-design/skills/README.md +6 -6
- package/modules/ui-design/skills/brandkit/SKILL.md +1 -1
- package/modules/ui-design/skills/design-taste-frontend/SKILL.md +1 -1
- package/modules/ui-design/skills/design-taste-frontend-v1/SKILL.md +1 -1
- package/modules/ui-design/skills/gpt-taste/SKILL.md +1 -1
- package/modules/ui-design/skills/high-end-visual-design/SKILL.md +1 -1
- package/modules/ui-design/skills/image-to-code/SKILL.md +1 -1
- package/modules/ui-design/skills/imagegen-frontend-mobile/SKILL.md +1 -1
- package/modules/ui-design/skills/imagegen-frontend-web/SKILL.md +1 -1
- package/modules/ui-design/skills/impeccable/SKILL.md +1 -1
- package/modules/ui-design/skills/industrial-brutalist-ui/SKILL.md +1 -1
- package/modules/ui-design/skills/minimalist-ui/SKILL.md +1 -1
- package/modules/ui-design/skills/redesign-existing-projects/SKILL.md +1 -1
- package/modules/ui-design/skills/stitch-design-taste/SKILL.md +1 -1
- package/modules/ui-design/skills/style/SKILL.md +2 -2
- package/modules/ui-design/styles/README.md +3 -3
- package/modules/ui-design/styles/blueprint/DESIGN.md +1 -1
- package/modules/ui-design/styles/chrome-world/DESIGN.md +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/adopt-preflight.js +68 -2
- package/scripts/gds/audit-rank-roles.js +172 -0
- package/scripts/gds/build-cli-package.js +2 -0
- package/scripts/gds/claim.js +94 -9
- package/scripts/gds/claude-md-template.js +18 -0
- package/scripts/gds/device-flow-help.js +72 -0
- package/scripts/gds/fitness.js +8 -8
- package/scripts/gds/go-live.js +139 -5
- package/scripts/gds/init.js +64 -8
- package/scripts/gds/land-watch.js +13 -5
- package/scripts/gds/login.js +18 -0
- package/scripts/gds/mirror-redact.js +13 -2
- package/scripts/gds/package-core.js +11 -3
- package/scripts/gds/release.js +55 -0
- package/scripts/gds/setup.js +5 -39
- package/scripts/gds/ship-land.js +10 -2
- package/scripts/gds/ship-regen.js +41 -8
- package/scripts/gds/start.js +7 -4
- package/scripts/gds/worktree-claim-guard.js +27 -1
- package/scripts/public-mirror-export.js +17 -4
- package/src/bongos/api-errors.js +9 -0
- package/src/bongos/artist-gate.js +204 -0
- package/src/bongos/repo-info.js +37 -6
- package/src/branding.js +15 -0
- package/src/module-api.js +41 -1
- package/tests/adopt_preflight.mjs +72 -0
- package/tests/api_path_404.mjs +28 -0
- package/tests/artist_gate.mjs +234 -0
- package/tests/artist_gate_release.mjs +283 -0
- package/tests/artist_gate_route.mjs +174 -0
- package/tests/bongos_login.mjs +69 -2
- package/tests/box_scope_predicate.mjs +117 -0
- package/tests/claim_error_surface.mjs +163 -1
- package/tests/claude_materialize.mjs +9 -0
- package/tests/cli_exit_no_abort.mjs +256 -3
- package/tests/currency_label.mjs +11 -4
- package/tests/discord_approvals.mjs +45 -0
- package/tests/go_live.mjs +144 -0
- package/tests/init.mjs +52 -2
- package/tests/main_worktree_busy_reason.mjs +167 -0
- package/tests/membership_kind_lockstep.mjs +88 -0
- package/tests/mirror_redact.mjs +14 -0
- package/tests/module_api.mjs +7 -0
- package/tests/provision_settings_apply.mjs +13 -5
- package/tests/provisioning_settings.mjs +11 -7
- package/tests/provisioning_settings_apply.mjs +4 -4
- package/tests/provisioning_settings_env.mjs +59 -7
- package/tests/public_mirror_export.mjs +12 -0
- package/tests/rank_roles_audit.mjs +114 -0
- package/tests/repo_info_binding.mjs +82 -0
- package/tests/scrubber_corpus.mjs +70 -2
- package/tests/setup_device_flow_fallback.mjs +16 -0
- package/tests/start_rebase_warning.mjs +50 -0
- package/tests/task_field_bounds_match_db.mjs +140 -0
package/scripts/gds/setup.js
CHANGED
|
@@ -274,45 +274,11 @@ async function promptOnboardingConsent() {
|
|
|
274
274
|
}
|
|
275
275
|
|
|
276
276
|
// ── W1: the manifest-born App has Device Flow OFF (task 1003051) ─────────────
|
|
277
|
-
//
|
|
278
|
-
//
|
|
279
|
-
//
|
|
280
|
-
//
|
|
281
|
-
|
|
282
|
-
// CLI-token path so setup still completes. Pure; exported for tests.
|
|
283
|
-
function isDeviceFlowDisabled(start) {
|
|
284
|
-
if (!start || start.ok) return false;
|
|
285
|
-
const err = start.data && start.data.error;
|
|
286
|
-
const text = [
|
|
287
|
-
typeof err === 'string' ? err : '',
|
|
288
|
-
err && typeof err === 'object' ? `${err.code || ''} ${err.message || ''}` : '',
|
|
289
|
-
start.data && start.data.message ? String(start.data.message) : '',
|
|
290
|
-
].join(' ');
|
|
291
|
-
return /device_flow_disabled/i.test(text);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// The plain-language fallback guidance (task 1003051): what happened, the web
|
|
295
|
-
// path that works right now, and the one-checkbox owner fix. Pure; exported.
|
|
296
|
-
function deviceFlowFallbackLines(base) {
|
|
297
|
-
const settings = `${base}/builders/settings`;
|
|
298
|
-
return [
|
|
299
|
-
'',
|
|
300
|
-
"This project's GitHub App has Device Flow switched off, so terminal sign-in",
|
|
301
|
-
'cannot start. Apps created by the one-click setup begin this way — GitHub',
|
|
302
|
-
'only offers the switch as a checkbox in the App settings; no setup flow can',
|
|
303
|
-
'tick it automatically.',
|
|
304
|
-
'',
|
|
305
|
-
'Sign in through the web instead (works right now):',
|
|
306
|
-
` 1. Open ${settings} and sign in with GitHub.`,
|
|
307
|
-
' 2. Under "CLI Access", click "Re-issue CLI token".',
|
|
308
|
-
' 3. Copy the token it shows and paste it below (or run the printed',
|
|
309
|
-
' one-liner in another shell, then re-run setup).',
|
|
310
|
-
'',
|
|
311
|
-
'To repair terminal sign-in for everyone (the project owner, once):',
|
|
312
|
-
' GitHub → Settings → Developer settings → GitHub Apps → your app →',
|
|
313
|
-
' check "Enable Device Flow" → Save. Web sign-in is unaffected either way.',
|
|
314
|
-
];
|
|
315
|
-
}
|
|
277
|
+
// The detector + the guidance moved to ./device-flow-help (task 1003400) so
|
|
278
|
+
// `bongos login` can use the same words without closing a require cycle back
|
|
279
|
+
// through this file (setup requires login for runDeviceFlow). Re-exported here
|
|
280
|
+
// because both are part of setup.js's tested public surface.
|
|
281
|
+
const { isDeviceFlowDisabled, deviceFlowFallbackLines } = require('./device-flow-help');
|
|
316
282
|
|
|
317
283
|
// Verify a hall-minted CLI token against /me and save the session (the
|
|
318
284
|
// paste-token.js contract: nothing is written unless the token verifies).
|
package/scripts/gds/ship-land.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
const fs = require('node:fs');
|
|
12
12
|
const os = require('node:os');
|
|
13
13
|
const path = require('node:path');
|
|
14
|
-
const { cliClient } = require('./cli-lib');
|
|
14
|
+
const { cliClient, isBoxScope } = require('./cli-lib');
|
|
15
15
|
const { gitArgvOk, gitOk, sanitizeGitHubRefs, shellOk } = require('./ship-git.js');
|
|
16
16
|
const { buildPrCreateArgv, buildPrMergeArgv, deployRunSucceeded, deployedCommitStatus, fetchDeployedVersion, ghArgvOk, ghJson, pushVia, resolveInstanceBase } = require('./ship-deploy-target.js');
|
|
17
17
|
const { normalizeApiError } = require('./ship-io.js');
|
|
@@ -184,7 +184,15 @@ async function ciLandServer(taskId, branch, valueSummary) {
|
|
|
184
184
|
id: taskId,
|
|
185
185
|
body: { branch, head_sha: headSha, value_summary: valueSummary, bundle_b64: bundleB64 },
|
|
186
186
|
});
|
|
187
|
-
|
|
187
|
+
// task 1002593: ONE definition of "is this a box-scope refusal". This site used
|
|
188
|
+
// to hand-roll the comparison, which is how it silently died when routes moved
|
|
189
|
+
// to the object error envelope — `r.data.error` stopped being the bare string
|
|
190
|
+
// and the branch could never fire. Normalizing here fixed the symptom but left
|
|
191
|
+
// a SECOND predicate for the same question, and two copies of a predicate are
|
|
192
|
+
// how one of them gets a fix the other never sees (migration 177 dropping 176's
|
|
193
|
+
// blocker clause is the same shape). cli-lib's isBoxScope is the one every
|
|
194
|
+
// other caller already uses; this is the last site that did not.
|
|
195
|
+
if (isBoxScope(r)) {
|
|
188
196
|
console.log(' ci-land: this session is BOX-SCOPED — run /builder-reauth (browser, no terminal) to mint a real session, then re-ship. Task stays at confirmed.');
|
|
189
197
|
return landBailed('this CLI session is box-scoped', '/builder-reauth');
|
|
190
198
|
}
|
|
@@ -248,17 +248,49 @@ function regenerateFileMap() {
|
|
|
248
248
|
// ship-vs-merge-mode and merge-mode-vs-merge-mode races that the in-ship.js
|
|
249
249
|
// lock could not cover. See docs/recipes/multi-builder-merge.md.
|
|
250
250
|
|
|
251
|
+
// describeMainWorktreeBusy — the PURE verdict. Given the three facts about
|
|
252
|
+
// REPO_ROOT, is it safe to mutate, and if not, why? Returns a human reason string
|
|
253
|
+
// or null.
|
|
254
|
+
//
|
|
255
|
+
// task 1002871: it used to answer only 'a merge is already in progress' or 'the
|
|
256
|
+
// working tree is dirty…', and it runs BEFORE the named-branch diagnostic in
|
|
257
|
+
// ship-merge.js. So the shape of the original incident — a shared main checkout
|
|
258
|
+
// that is BOTH parked on a feature branch AND dirty (2026-07-28: parked on
|
|
259
|
+
// `task-1002503-backfill-recovery` with 2 unpushed commits and 4 modified
|
|
260
|
+
// generated files, blocking the auto-merge leg for 9 confirmed tasks) —
|
|
261
|
+
// short-circuited here and the builder never learned WHICH branch was squatting.
|
|
262
|
+
// The named-branch message was only reachable when the tree was clean, which is
|
|
263
|
+
// the one case where nobody needs to be told twice.
|
|
264
|
+
//
|
|
265
|
+
// Pure + exported so the wording is unit-tested rather than eyeballed: this is the
|
|
266
|
+
// ship hot path, and the whole value of the change is the exact string.
|
|
267
|
+
function describeMainWorktreeBusy({ mergeInProgress, dirty, branch, defaultBranch = 'main' } = {}) {
|
|
268
|
+
// A branch name is only worth naming when it is NOT the expected one. Saying
|
|
269
|
+
// "on main" in every bail would be noise that trains the reader to skip the line.
|
|
270
|
+
const occupant = branch && branch !== defaultBranch && branch !== 'HEAD'
|
|
271
|
+
? ` (on \`${branch}\`, not \`${defaultBranch}\`)`
|
|
272
|
+
: '';
|
|
273
|
+
if (mergeInProgress) return `a merge is already in progress${occupant}`;
|
|
274
|
+
if (dirty) return `the working tree is dirty or has unmerged files${occupant}`;
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
|
|
251
278
|
// REPO_ROOT must be on main, not mid-merge, and clean before we mutate it.
|
|
252
|
-
// Returns a human reason string if busy, or null if safe to proceed.
|
|
279
|
+
// Returns a human reason string if busy, or null if safe to proceed. The I/O
|
|
280
|
+
// shell over describeMainWorktreeBusy — gather the facts, let the pure half decide.
|
|
253
281
|
function mainWorktreeBusy() {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
282
|
+
const mergeInProgress = !!gitOk('git rev-parse -q --verify MERGE_HEAD', { cwd: REPO_ROOT });
|
|
283
|
+
// Short-circuit the moment the verdict is decided. `describeMainWorktreeBusy`
|
|
284
|
+
// prefers merge-in-progress over dirty, so once that is true the dirty probe's
|
|
285
|
+
// answer is discarded — spawning it anyway would be two subprocesses bought for
|
|
286
|
+
// nothing. The branch IS still needed on this arm: the reason names it either way.
|
|
287
|
+
const branch = gitOk('git rev-parse --abbrev-ref HEAD', { cwd: REPO_ROOT }) || null;
|
|
288
|
+
if (mergeInProgress) return describeMainWorktreeBusy({ mergeInProgress: true, branch });
|
|
257
289
|
const dirty = gitOk('git status --porcelain', { cwd: REPO_ROOT });
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
return
|
|
290
|
+
// Best-effort throughout: a probe failure yields null, which the pure half reads
|
|
291
|
+
// as "no occupant worth naming". A diagnostic must never be the thing that
|
|
292
|
+
// breaks the bail it is decorating.
|
|
293
|
+
return describeMainWorktreeBusy({ mergeInProgress: false, dirty: !!(dirty && dirty.length > 0), branch });
|
|
262
294
|
}
|
|
263
295
|
|
|
264
296
|
// task 391 (idea #37): the verdict for a ship whose branch has no commits ahead
|
|
@@ -276,6 +308,7 @@ module.exports = {
|
|
|
276
308
|
VENDORED_CLIENT_REL,
|
|
277
309
|
apiClientPathspec,
|
|
278
310
|
currentBranch,
|
|
311
|
+
describeMainWorktreeBusy,
|
|
279
312
|
mainWorktreeBusy,
|
|
280
313
|
regenerateApiClient,
|
|
281
314
|
regenerateApiDocs,
|
package/scripts/gds/start.js
CHANGED
|
@@ -272,8 +272,9 @@ function rebaseWarningModel(needsRebaseTasks) {
|
|
|
272
272
|
function rebaseWarningMarkdown(needsRebaseTasks) {
|
|
273
273
|
const w = rebaseWarningModel(needsRebaseTasks);
|
|
274
274
|
if (!w) return null;
|
|
275
|
-
return `⚠️ ${surface.strong('
|
|
276
|
-
+ ` until you
|
|
275
|
+
return `⚠️ ${surface.strong('Land owed — your queue is GATED.')} ${w.count} ${w.noun} can't land and block new claims`
|
|
276
|
+
+ ` until you ${surface.code('git merge origin/main')} + re-ship (or ${surface.code(surface.action('release'))}) ${w.pronoun}: ${w.refs}.`
|
|
277
|
+
+ ' Merge, never rebase — a published branch that is rewritten cannot be pushed again (task 1002788).';
|
|
277
278
|
}
|
|
278
279
|
|
|
279
280
|
// The widget warning block (Tabler alert icon, danger-tinted), or '' when none.
|
|
@@ -281,7 +282,7 @@ function rebaseWarningWidget(needsRebaseTasks) {
|
|
|
281
282
|
const w = rebaseWarningModel(needsRebaseTasks);
|
|
282
283
|
if (!w) return '';
|
|
283
284
|
return `<div class="bs-warn"><i class="ti ti-alert-triangle" aria-hidden="true"></i>`
|
|
284
|
-
+ `<span><strong>
|
|
285
|
+
+ `<span><strong>Land owed — your queue is GATED.</strong> ${w.count} ${w.noun} can't land and block new claims until you <code>git merge origin/main</code> + re-ship (or /builder-release) ${w.pronoun}: ${hesc(w.refs)}. Merge, never rebase — a published branch that is rewritten cannot be pushed again (task 1002788).</span></div>`;
|
|
285
286
|
}
|
|
286
287
|
|
|
287
288
|
// Run console.log-based helpers (printCostWarning/printOnboardingNudge) without
|
|
@@ -832,7 +833,9 @@ async function main() {
|
|
|
832
833
|
|
|
833
834
|
// ADR 0120 part 5: the owed-rebase gate warning — rendered directly under the
|
|
834
835
|
// identity line and before the claimable table, since it BLOCKS new claims
|
|
835
|
-
// until the builder lands (
|
|
836
|
+
// until the builder lands (`git merge origin/main` + re-ship — task 1002788:
|
|
837
|
+
// MERGE, because the branch is already published and the server's publish is
|
|
838
|
+
// append-only, so a rebase makes it permanently un-pushable) or releases them.
|
|
836
839
|
const rebaseLine = rebaseWarningMarkdown(needsRebaseTasks);
|
|
837
840
|
if (rebaseLine) {
|
|
838
841
|
console.log('');
|
|
@@ -101,4 +101,30 @@ function decideWorktreeClaim({
|
|
|
101
101
|
};
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
// task 1002871 — the OTHER end of the same failure. decideWorktreeClaim stops a
|
|
105
|
+
// builder parking the main checkout at CLAIM time; this speaks at RELEASE time,
|
|
106
|
+
// when a claim worked under the `--here` hatch is given up and the main checkout
|
|
107
|
+
// is left holding the feature branch. Nobody is watching that tree, and the next
|
|
108
|
+
// ship to reach for it — possibly another builder's — is who pays: the 2026-07-28
|
|
109
|
+
// incident was one parked checkout blocking the auto-merge leg for 9 tasks.
|
|
110
|
+
//
|
|
111
|
+
// IT WARNS AND NEVER RESTORES, deliberately. Switching the tree back to main
|
|
112
|
+
// would be a destructive guess: the parked branch may hold real uncommitted work,
|
|
113
|
+
// and release.js has no way to tell that from debris. Naming it is enough — the
|
|
114
|
+
// person reading has the context to decide.
|
|
115
|
+
//
|
|
116
|
+
// Pure; returns an array of lines (empty when there is nothing to say).
|
|
117
|
+
function parkedMainCheckoutWarning({ branch, defaultBranch = 'main', dirty = false } = {}) {
|
|
118
|
+
if (!branch || branch === defaultBranch || branch === 'HEAD') return [];
|
|
119
|
+
return [
|
|
120
|
+
`⚠ The main checkout is still on \`${branch}\`, not \`${defaultBranch}\`.`,
|
|
121
|
+
dirty
|
|
122
|
+
? ' It also has uncommitted changes, so nothing here will touch it.'
|
|
123
|
+
: ' Nothing here will touch it — the branch may hold work you still want.',
|
|
124
|
+
` While it stays parked, the local auto-merge at ship time has nowhere to merge INTO,`,
|
|
125
|
+
' so the next ship on this machine strands at `confirmed` — including someone else\'s.',
|
|
126
|
+
` When you are done with it: git -C <main checkout> checkout ${defaultBranch}`,
|
|
127
|
+
];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
module.exports = { decideWorktreeClaim, parkedMainCheckoutWarning, ESCAPE_FLAG };
|
|
@@ -95,17 +95,23 @@ function isBinary(buf) {
|
|
|
95
95
|
// over their latin1 view (so an embedded literal still trips the gate). The gate uses
|
|
96
96
|
// `sensitiveLiterals` (raw substrings), NOT `rules` — stricter than redaction on
|
|
97
97
|
// purpose (see mirror-redact.js). Returns { buf:Buffer, leaks:[{label,sample}], secret:boolean }.
|
|
98
|
-
function redactBlob({ buf, rules, sensitiveLiterals, envSecrets }) {
|
|
98
|
+
function redactBlob({ buf, rules, sensitiveLiterals, envSecrets, knownDocNames }) {
|
|
99
99
|
if (isBinary(buf)) {
|
|
100
100
|
const view = buf.toString('latin1');
|
|
101
101
|
return { buf, leaks: findLeaks(view, sensitiveLiterals), secret: findSecretLeak(view, envSecrets) };
|
|
102
102
|
}
|
|
103
103
|
const redacted = redactText(buf.toString('utf8'), rules);
|
|
104
|
-
|
|
104
|
+
// task 1002460: the set of filenames THIS publish is shipping. The entropy
|
|
105
|
+
// sweep mistakes a long doc filename for a secret, and only the caller knows
|
|
106
|
+
// which names are real files. With no set there is no exemption at all.
|
|
107
|
+
const scrubbed = scrubSecrets(redacted, envSecrets, { knownDocNames });
|
|
105
108
|
return {
|
|
106
109
|
buf: Buffer.from(scrubbed, 'utf8'),
|
|
107
110
|
leaks: findLeaks(scrubbed, sensitiveLiterals),
|
|
108
|
-
|
|
111
|
+
// Same allowlist as the scrub directly above, and it has to be: findSecretLeak is a
|
|
112
|
+
// differential audit of THAT scrub's output, so an option only one of the two holds
|
|
113
|
+
// is read as a leak in every doc (task 1003873). Keep these two calls in step.
|
|
114
|
+
secret: findSecretLeak(scrubbed, envSecrets, { knownDocNames }),
|
|
109
115
|
};
|
|
110
116
|
}
|
|
111
117
|
|
|
@@ -206,6 +212,13 @@ function main() {
|
|
|
206
212
|
|
|
207
213
|
// 4. Redact every file IN MEMORY and run the fail-closed no-leak gate. Nothing touches
|
|
208
214
|
// the mirror filesystem until the ENTIRE set is proven clean (atomic abort).
|
|
215
|
+
// task 1002460 — the doc-link allowlist, computed for THIS export. Without it the
|
|
216
|
+
// entropy sweep cannot tell a long ADR filename from a secret and collapses every
|
|
217
|
+
// one to <redacted>.md. This is the mirror`s own file set, so a name in it is a
|
|
218
|
+
// FILE and not a guess; anything else, secrets included, redacts exactly as before.
|
|
219
|
+
const knownDocNames = new Set(
|
|
220
|
+
publishable.map((e) => e.path.split('/').pop()).filter((n) => /\.(md|sql)$/i.test(n)),
|
|
221
|
+
);
|
|
209
222
|
const staged = []; // { path, buf, mode }
|
|
210
223
|
const leaks = []; // { path, label, sample? }
|
|
211
224
|
for (const e of publishable) {
|
|
@@ -216,7 +229,7 @@ function main() {
|
|
|
216
229
|
leaks.push({ path: e.path, label: `read-failed:${err.message}` });
|
|
217
230
|
continue;
|
|
218
231
|
}
|
|
219
|
-
const { buf, leaks: fileLeaks, secret } = redactBlob({ buf: raw, rules, sensitiveLiterals, envSecrets });
|
|
232
|
+
const { buf, leaks: fileLeaks, secret } = redactBlob({ buf: raw, rules, sensitiveLiterals, envSecrets, knownDocNames });
|
|
220
233
|
for (const l of fileLeaks) leaks.push({ path: e.path, label: l.label, sample: l.sample });
|
|
221
234
|
if (secret) leaks.push({ path: e.path, label: 'secret-shape' }); // never print the secret sample
|
|
222
235
|
staged.push({ path: e.path, buf, mode: e.mode });
|
package/src/bongos/api-errors.js
CHANGED
|
@@ -167,6 +167,15 @@ const OVERRIDES = Object.freeze({
|
|
|
167
167
|
// --- validation ---
|
|
168
168
|
validation_failed: 'validation_failed',
|
|
169
169
|
schema_error: 'validation_failed',
|
|
170
|
+
|
|
171
|
+
// task 1002845: a Postgres CHECK violation surfaced as a 400 that names the
|
|
172
|
+
// constraint. It needs an explicit entry because the heuristics below would
|
|
173
|
+
// classify it `internal` (no matching prefix/suffix rule), which is the safe
|
|
174
|
+
// default for an unknown code but wrong here — the caller CAN fix this by
|
|
175
|
+
// changing the value they sent, so it must be bad_request and non-retriable.
|
|
176
|
+
// An unregistered code would still have answered 400 (the call site passes an
|
|
177
|
+
// explicit status), leaving the category quietly disagreeing with the status.
|
|
178
|
+
constraint_violation: 'bad_request',
|
|
170
179
|
});
|
|
171
180
|
|
|
172
181
|
// Suffix/prefix heuristic for the long tail. Order matters — first match wins.
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/bongos/artist-gate.js — the artist gate: what an unresolved artist review
|
|
4
|
+
// holds, and what releases it (ADR 0241, task 1003575).
|
|
5
|
+
//
|
|
6
|
+
// WHAT IT ANSWERS: "an artist review task is open — does anything stop?" One
|
|
7
|
+
// value, read from one place, so the cascade that FILES those reviews and the
|
|
8
|
+
// deploy that they HOLD cannot disagree about the project's own policy:
|
|
9
|
+
//
|
|
10
|
+
// off no artist review tasks are filed at all; nothing is held
|
|
11
|
+
// advisory reviews are filed; nothing is held
|
|
12
|
+
// strict reviews are filed, and an unresolved one holds the DEPLOY
|
|
13
|
+
//
|
|
14
|
+
// THE TWO THINGS THAT MAKE THIS ADMISSIBLE, both from ADR 0241 and neither
|
|
15
|
+
// optional — a gate that loses either one is the gate ADR 0162 retired:
|
|
16
|
+
//
|
|
17
|
+
// 1. IT READS A STATE, NEVER A VERDICT. The blocking condition is "is there an
|
|
18
|
+
// unresolved artist review attached to this work?", never "did the artist
|
|
19
|
+
// approve it?". An artist saying the copy is bad stops nothing by itself, and
|
|
20
|
+
// an artist saying it is fine releases nothing by itself; what moves is the
|
|
21
|
+
// review task's STATUS, by the ordinary means (shipped, or dismissed in
|
|
22
|
+
// triage). That is what keeps the gate rank-consistent and machine-checkable
|
|
23
|
+
// instead of pinned to one person being available.
|
|
24
|
+
//
|
|
25
|
+
// 2. IT HOLDS THE DEPLOY, NOT THE SHIP. A builder's work still grades, merges,
|
|
26
|
+
// credits and lands on CI alone. Nothing here may ever reach the ship or land
|
|
27
|
+
// path — the wave-4 strand class that forced ADR 0162 is exactly what that
|
|
28
|
+
// separation prevents. The only thing this can delay is a pin move, which is
|
|
29
|
+
// already deliberate, already owner-initiated, and reversible by doing it
|
|
30
|
+
// later.
|
|
31
|
+
//
|
|
32
|
+
// PURE HALF / READ HALF. Everything below `unresolvedArtistReviews` is pure — a
|
|
33
|
+
// list of task-shaped objects in, a verdict out — so the halt matrix tests with no
|
|
34
|
+
// pack, no env and no database. Only `artistGateMode` touches the branding pack,
|
|
35
|
+
// and it defers that require to call time for the same reason project-door.js
|
|
36
|
+
// does.
|
|
37
|
+
|
|
38
|
+
const log = require('./logger').logger.child({ src: 'artist-gate' });
|
|
39
|
+
|
|
40
|
+
// The three levels, ORDERED loosest → strictest (ADR 0241 §2's table).
|
|
41
|
+
const ARTIST_GATE_LEVELS = Object.freeze(['off', 'advisory', 'strict']);
|
|
42
|
+
|
|
43
|
+
// A task status is RESOLVED when the work it asks for is done or the board has
|
|
44
|
+
// decided it will not be — 'shipped' and 'abandoned' are the two ways a task
|
|
45
|
+
// leaves the board, and 'abandoned' is what triage's dismiss writes. Everything
|
|
46
|
+
// else (backlog, ready, active, completed, confirmed) is an open request, which
|
|
47
|
+
// is the state ADR 0241 §1 says the gate reads. A row merged into another is
|
|
48
|
+
// resolved too: its successor carries the work.
|
|
49
|
+
const RESOLVED_REVIEW_STATUSES = Object.freeze(['shipped', 'abandoned']);
|
|
50
|
+
|
|
51
|
+
// The (source, source_ref-prefix) pair every cascade-filed artist review carries.
|
|
52
|
+
// Mirrored from modules/lifecycle/cascade.js rather than imported: core must not
|
|
53
|
+
// reach into a module (the fitness boundary), and a review's identity on the wire
|
|
54
|
+
// is a two-field fact, not a behaviour. The cascade's own test pins the spelling.
|
|
55
|
+
const ARTIST_REVIEW_SOURCE = 'cascade';
|
|
56
|
+
const ARTIST_REVIEW_REF_PREFIX = 'cascade-artist-review-on-ship-';
|
|
57
|
+
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// The pure half
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
// Is this task row a cascade-filed artist review? PURE and total.
|
|
63
|
+
function isArtistReview(task) {
|
|
64
|
+
if (!task) return false;
|
|
65
|
+
return String(task.source || '') === ARTIST_REVIEW_SOURCE
|
|
66
|
+
&& String(task.source_ref || '').startsWith(ARTIST_REVIEW_REF_PREFIX);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Is this review still an open request? PURE and total. A merged row counts as
|
|
70
|
+
// resolved even at an open status — the merge moved the work, and holding a
|
|
71
|
+
// deploy on a row nobody will ever action again is the stall this gate must not
|
|
72
|
+
// have.
|
|
73
|
+
function isReviewResolved(task) {
|
|
74
|
+
if (!task) return true;
|
|
75
|
+
if (task.merged_into_task_id != null) return true;
|
|
76
|
+
return RESOLVED_REVIEW_STATUSES.includes(String(task.status || ''));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// The reviews that actually HOLD, out of every review row handed in. PURE.
|
|
80
|
+
//
|
|
81
|
+
// Two filters, and the second is ADR 0241 §5 — tightening is not retroactive. A
|
|
82
|
+
// project that raises its gate to 'strict' must not inherit a backlog of reviews
|
|
83
|
+
// for work nobody remembers, so a review filed BEFORE the raise never holds. An
|
|
84
|
+
// absent or unparseable `since` means no cutoff, which is the right answer for a
|
|
85
|
+
// project that has been strict since it stood up (the default) — it is not a
|
|
86
|
+
// licence to ignore reviews, so it must not read as one.
|
|
87
|
+
//
|
|
88
|
+
// A review with no readable created_at is KEPT: the cutoff exists to spare a
|
|
89
|
+
// project work it never chose, not to let an unreadable row slip the gate.
|
|
90
|
+
function unresolvedArtistReviews(tasks, { since = null } = {}) {
|
|
91
|
+
const cutoff = since ? Date.parse(since) : NaN;
|
|
92
|
+
const list = Array.isArray(tasks) ? tasks : [];
|
|
93
|
+
return list.filter((t) => {
|
|
94
|
+
if (!isArtistReview(t) || isReviewResolved(t)) return false;
|
|
95
|
+
if (Number.isNaN(cutoff)) return true;
|
|
96
|
+
const filed = Date.parse(t.created_at);
|
|
97
|
+
if (Number.isNaN(filed)) return true;
|
|
98
|
+
return filed >= cutoff;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Does the gate hold, and if so WHAT holds it and WHICH control releases it?
|
|
103
|
+
// Returns null when nothing is held, else a halt-shaped object. PURE.
|
|
104
|
+
//
|
|
105
|
+
// The sentence is the point, not decoration. ADR 0241 §4's second guard is
|
|
106
|
+
// explicit: the refusal must name what holds the deploy and which control
|
|
107
|
+
// releases it, in one line, because ADR 0162's incident was made worse by a
|
|
108
|
+
// blocked state that did not explain itself. So the detail names the review ids
|
|
109
|
+
// AND both real ways out — resolve them, or the project OWNER releases past with
|
|
110
|
+
// an override-request (rationale required, audit-logged; a self-release is
|
|
111
|
+
// permitted and recorded as such, ADR 0241 §3).
|
|
112
|
+
//
|
|
113
|
+
// Keep this sentence TRUE to the mechanism, not merely to the ADR's prose. The
|
|
114
|
+
// first cut of this gate described an escape hatch the code did not wire, which
|
|
115
|
+
// is worse than no sentence: a refusal that names a control nobody can actually
|
|
116
|
+
// use is a stall with extra steps. The release lives in
|
|
117
|
+
// modules/lifecycle/db-overrides.js (the gate-release class) and the gate reads
|
|
118
|
+
// it through db-tasks.listOpenArtistReviews — change one, re-read the other two.
|
|
119
|
+
// `truncated` says the caller's read hit its scan cap, so `reviews` is a floor
|
|
120
|
+
// rather than the whole queue. It cannot change the VERDICT — every row handed in
|
|
121
|
+
// is a holder, so one is enough — only the count, which is then reported as a
|
|
122
|
+
// floor ("200+") instead of a number the sentence cannot stand behind.
|
|
123
|
+
function artistGateHalt({ mode, reviews = [], truncated = false } = {}) {
|
|
124
|
+
if (mode !== 'strict') return null;
|
|
125
|
+
const open = Array.isArray(reviews) ? reviews : [];
|
|
126
|
+
if (!open.length) return null;
|
|
127
|
+
// Named as `task N`, never a bare `#N`: this sentence is copied into chat, commit
|
|
128
|
+
// messages and session logs, where a bare hash autolinks to a GitHub issue that
|
|
129
|
+
// does not exist (the kernel's linkify rule).
|
|
130
|
+
const ids = open.slice(0, 5).map((t) => String(t.id));
|
|
131
|
+
const more = open.length - ids.length;
|
|
132
|
+
const named = ids.map((id) => `task ${id}`).join(', ');
|
|
133
|
+
return {
|
|
134
|
+
code: 'ARTIST_REVIEW_UNRESOLVED',
|
|
135
|
+
message: `${open.length}${truncated ? '+' : ''} unresolved artist review${open.length === 1 && !truncated ? '' : 's'} hold this deploy — the project runs artist_gate 'strict'`,
|
|
136
|
+
detail: `Held by ${named}${more > 0 ? ` and ${more} more` : ''}. `
|
|
137
|
+
+ 'Two ways out, both ordinary: resolve or dismiss those reviews on the board, '
|
|
138
|
+
+ 'or release one — POST /tasks/:id/override-request with a rationale, then the PROJECT OWNER '
|
|
139
|
+
+ 'approves it at POST /override-requests/:id/decide (owner only, not an Archon; a self-release '
|
|
140
|
+
+ 'is allowed and is audit-logged as a self-override). A released review stays open on the board; '
|
|
141
|
+
+ "what it stops doing is holding the pin. Setting this project's artist_gate to 'advisory' "
|
|
142
|
+
+ 'stops reviews holding deploys at all.',
|
|
143
|
+
reviewIds: open.map((t) => String(t.id)),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
// The read half
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
|
|
151
|
+
// artistGateMode — this project's stored artist strictness, and ONLY that. The
|
|
152
|
+
// one reader for the knob, so the cascade, the deploy gate and any surface that
|
|
153
|
+
// reports the policy cannot disagree about what the owner set.
|
|
154
|
+
//
|
|
155
|
+
// An unreadable pack, or a value this core cannot read, resolves to 'strict'. That
|
|
156
|
+
// is fail-closed in the direction ADR 0241 §4 already chose as the DEFAULT, so an
|
|
157
|
+
// instance whose pack went missing behaves like a fresh one rather than silently
|
|
158
|
+
// dropping a policy the owner set — and 'strict' holds only a pin, which the owner
|
|
159
|
+
// can always release. `read` is injectable for the unit tests only (the pack is
|
|
160
|
+
// memoized for the process lifetime, which is why applying a change restarts the
|
|
161
|
+
// instance).
|
|
162
|
+
let warnedUnknownArtistGate = false;
|
|
163
|
+
function artistGateMode(read) {
|
|
164
|
+
const readPack = read || require('../branding').branding; // eslint-disable-line global-require
|
|
165
|
+
let v;
|
|
166
|
+
try { v = readPack().project?.artistGate; } catch (err) {
|
|
167
|
+
log.error('[artist-gate] branding pack unreadable — treating the project as strict:', err.message);
|
|
168
|
+
return 'strict';
|
|
169
|
+
}
|
|
170
|
+
if (ARTIST_GATE_LEVELS.includes(v)) return v;
|
|
171
|
+
// Absent is the honest case for a self-hosted instance that has never been
|
|
172
|
+
// pushed a setting, and for the neutral pack before a value was written. It
|
|
173
|
+
// takes the default rather than a warning.
|
|
174
|
+
if (v === undefined || v === null || v === '') return 'strict';
|
|
175
|
+
if (!warnedUnknownArtistGate) {
|
|
176
|
+
warnedUnknownArtistGate = true;
|
|
177
|
+
log.error(`[artist-gate] unrecognised artistGate ${JSON.stringify(v)} — treating the project as strict`);
|
|
178
|
+
}
|
|
179
|
+
return 'strict';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// The moment this project's gate was last RAISED to 'strict', or null. Written by
|
|
183
|
+
// the platform when the owner tightens the setting (provisioning.artistGateStamp)
|
|
184
|
+
// and carried in on <PREFIX>_ARTIST_GATE_SINCE. null = no cutoff (ADR 0241 §5).
|
|
185
|
+
function artistGateSince(read) {
|
|
186
|
+
const readPack = read || require('../branding').branding; // eslint-disable-line global-require
|
|
187
|
+
let v;
|
|
188
|
+
try { v = readPack().project?.artistGateSince; } catch { return null; }
|
|
189
|
+
if (typeof v !== 'string' || !v) return null;
|
|
190
|
+
return Number.isNaN(Date.parse(v)) ? null : v;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
module.exports = {
|
|
194
|
+
ARTIST_GATE_LEVELS,
|
|
195
|
+
RESOLVED_REVIEW_STATUSES,
|
|
196
|
+
ARTIST_REVIEW_SOURCE,
|
|
197
|
+
ARTIST_REVIEW_REF_PREFIX,
|
|
198
|
+
isArtistReview,
|
|
199
|
+
isReviewResolved,
|
|
200
|
+
unresolvedArtistReviews,
|
|
201
|
+
artistGateHalt,
|
|
202
|
+
artistGateMode,
|
|
203
|
+
artistGateSince,
|
|
204
|
+
};
|
package/src/bongos/repo-info.js
CHANGED
|
@@ -12,10 +12,11 @@
|
|
|
12
12
|
// transfers, the deploy's git remote changes and the served links follow,
|
|
13
13
|
// with zero code edits required.
|
|
14
14
|
//
|
|
15
|
-
// Cached on first call.
|
|
16
|
-
// OTB_GITHUB_REPO / OTB_GITHUB_BRANCH
|
|
17
|
-
//
|
|
18
|
-
//
|
|
15
|
+
// Cached on first call. Three sources, in order: env vars (OTB_GITHUB_OWNER /
|
|
16
|
+
// OTB_GITHUB_REPO / OTB_GITHUB_BRANCH — the operator's explicit override), the git
|
|
17
|
+
// remote when this package IS the repo, then the instance's committed branding pack
|
|
18
|
+
// (config/branding.json `repo`) — the source a standalone instance actually has.
|
|
19
|
+
// Returns { error } when none resolves, so callers can degrade gracefully.
|
|
19
20
|
|
|
20
21
|
const { execSync } = require('node:child_process');
|
|
21
22
|
const path = require('node:path');
|
|
@@ -44,6 +45,34 @@ function loadFromEnv() {
|
|
|
44
45
|
return null;
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
// The instance's OWN declared GitHub binding, read from the committed branding pack
|
|
49
|
+
// (`config/branding.json` -> `repo: { owner, name }` — a first-class key in the
|
|
50
|
+
// branding contract). `bongos init` writes it at scaffold time, so a standalone
|
|
51
|
+
// instance carries the correct answer on disk from birth.
|
|
52
|
+
//
|
|
53
|
+
// WHY it exists: an installed core (ADR 0140) lives in node_modules/@bongos/core, so
|
|
54
|
+
// loadFromGit's anchor rule correctly returns null — and nothing sets
|
|
55
|
+
// <PREFIX>_GITHUB_OWNER/REPO on a provisioned instance. EVERY standalone instance
|
|
56
|
+
// therefore resolved no_repo_info and served a 503 from /public/repo-info, with public
|
|
57
|
+
// build status and every repo-linked surface dead. The identity was one file away the
|
|
58
|
+
// whole time, simply never read.
|
|
59
|
+
//
|
|
60
|
+
// Placed LAST in the chain on purpose: the env pin stays the operator's explicit
|
|
61
|
+
// override and git stays authoritative for the core dev checkout, so this source can
|
|
62
|
+
// only ever turn an ERROR into an answer — it can never change one that already
|
|
63
|
+
// resolved. Required lazily so module load order never depends on branding, and
|
|
64
|
+
// fail-soft: a malformed or absent pack must degrade to no_repo_info, not crash boot.
|
|
65
|
+
function loadFromBranding(deps = {}) {
|
|
66
|
+
try {
|
|
67
|
+
const read = deps.branding || require('../branding').branding;
|
|
68
|
+
const pack = read() || {};
|
|
69
|
+
const owner = pack.repo && String(pack.repo.owner || '').trim();
|
|
70
|
+
const name = pack.repo && String(pack.repo.name || '').trim();
|
|
71
|
+
if (owner && name) return { owner, repo: name };
|
|
72
|
+
} catch (_) { /* fall through — an unreadable pack is not an answer */ }
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
47
76
|
// The directory this file's package occupies: <root>/src/bongos -> <root>. In the core
|
|
48
77
|
// dev checkout that is the repo root; installed, it is node_modules/@bongos/core.
|
|
49
78
|
const PACKAGE_ROOT = path.resolve(__dirname, '..', '..');
|
|
@@ -92,12 +121,13 @@ function loadFromGit(deps = {}) {
|
|
|
92
121
|
function loadRepoInfo(deps = {}) {
|
|
93
122
|
if (cached) return cached;
|
|
94
123
|
const branch = ic.resolveEnv('GITHUB_BRANCH') || 'main';
|
|
95
|
-
const parsed = loadFromEnv() || loadFromGit(deps);
|
|
124
|
+
const parsed = loadFromEnv() || loadFromGit(deps) || loadFromBranding(deps);
|
|
96
125
|
if (!parsed) {
|
|
97
126
|
cached = {
|
|
98
127
|
error: 'no_repo_info',
|
|
99
128
|
detail:
|
|
100
|
-
'git remote origin not resolvable from this package (an installed core does not sit inside the repo it publishes to)
|
|
129
|
+
'git remote origin not resolvable from this package (an installed core does not sit inside the repo it publishes to), '
|
|
130
|
+
+ 'config/branding.json has no repo.owner/repo.name, and <PREFIX>_GITHUB_OWNER/REPO unset',
|
|
101
131
|
};
|
|
102
132
|
return cached;
|
|
103
133
|
}
|
|
@@ -123,5 +153,6 @@ module.exports = {
|
|
|
123
153
|
loadRepoInfo,
|
|
124
154
|
parseRemote,
|
|
125
155
|
loadFromGit, // exported for unit tests — the anchor rule is the whole fix
|
|
156
|
+
loadFromBranding, // exported for unit tests — the standalone-instance source
|
|
126
157
|
_resetCacheForTests,
|
|
127
158
|
};
|
package/src/branding.js
CHANGED
|
@@ -69,6 +69,14 @@ const ENV_OVERRIDES = [
|
|
|
69
69
|
// view | apply | full. The ceiling on the visibility axis that JOINABILITY may
|
|
70
70
|
// only narrow; composed into one door by src/bongos/project-door.js.
|
|
71
71
|
['JOIN_GRANT', ['project', 'joinGrant']],
|
|
72
|
+
// The artist gate (ADR 0241, task 1003575) — off | advisory | strict. `off`
|
|
73
|
+
// generates no artist review tasks; `strict` lets an unresolved one hold this
|
|
74
|
+
// instance's DEPLOY (never a builder's ship). Its companion stamp says when the
|
|
75
|
+
// gate was last raised, so the hold is not retroactive (ADR 0241 §5); empty is
|
|
76
|
+
// skipped by the loop below, which reads as "no cutoff" — correct for a project
|
|
77
|
+
// that has been strict since it stood up.
|
|
78
|
+
['ARTIST_GATE', ['project', 'artistGate']],
|
|
79
|
+
['ARTIST_GATE_SINCE', ['project', 'artistGateSince']],
|
|
72
80
|
];
|
|
73
81
|
|
|
74
82
|
const isObj = (v) => v && typeof v === 'object' && !Array.isArray(v);
|
|
@@ -250,6 +258,13 @@ function clientBranding(b = branding()) {
|
|
|
250
258
|
// stranger's client should be able to say what THIS door does before knocking.
|
|
251
259
|
// `full` = this axis narrows nothing, so an un-pushed instance is unchanged.
|
|
252
260
|
joinGrant: b.project?.joinGrant || 'full',
|
|
261
|
+
// The artist gate (ADR 0241, task 1003575). Public for the read-back reason
|
|
262
|
+
// its neighbours are: a manage page has to be able to tell a SAVED setting
|
|
263
|
+
// from a LIVE one, and this instance's own manifest is the only honest
|
|
264
|
+
// answer to "what did it restart with?". The fallback is the platform
|
|
265
|
+
// DEFAULT, which for this one key is 'strict' rather than today's behaviour
|
|
266
|
+
// — the owner's ruling, ADR 0241 §4.
|
|
267
|
+
artistGate: b.project?.artistGate || 'strict',
|
|
253
268
|
},
|
|
254
269
|
};
|
|
255
270
|
}
|