@bongos/core 1.19.714 → 1.19.724
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 +488 -413
- 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/copy-inventory.md +15 -13
- package/docs/copy-registry.json +35 -17
- 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/hall-ui/public/watch.css +6 -0
- package/modules/hall-ui/public/watch.js +52 -6
- 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/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/audit-rank-roles.js +172 -0
- package/scripts/gds/backfill-task-rewards.js +9 -4
- package/scripts/gds/build-cli-package.js +1 -0
- package/scripts/gds/claim.js +94 -9
- package/scripts/gds/fitness.js +8 -8
- package/scripts/gds/go-live.js +139 -5
- package/scripts/gds/land-watch.js +13 -5
- 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/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/branding.js +15 -0
- package/src/module-api.js +34 -1
- 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/box_scope_predicate.mjs +117 -0
- package/tests/claim_error_surface.mjs +163 -1
- package/tests/cli_exit_no_abort.mjs +256 -3
- package/tests/credit_log_single_writer.mjs +94 -0
- package/tests/currency_label.mjs +11 -4
- package/tests/go_live.mjs +144 -0
- package/tests/helpers.mjs +8 -0
- 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 +6 -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/scrubber_corpus.mjs +70 -2
- package/tests/start_rebase_warning.mjs +50 -0
- package/tests/task_field_bounds_match_db.mjs +140 -0
- package/tests/watch_roster_self_row.mjs +289 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// modules/lifecycle/routes/artist-gate.js — the artist gate's STATE, in one read
|
|
4
|
+
// (ADR 0241, task 1003575).
|
|
5
|
+
//
|
|
6
|
+
// WHY A ROUTE. The gate's two halves live on opposite sides of the wire. The
|
|
7
|
+
// POLICY (`artist_gate`) is pushed into this instance's branding pack by the
|
|
8
|
+
// platform and read by the core; the STATE it reads — which artist review tasks
|
|
9
|
+
// are still open — is this instance's own board. The thing that has to consult
|
|
10
|
+
// both is `scripts/gds/go-live.js`, the pin move, which runs from a core checkout
|
|
11
|
+
// against a LIVE instance and can see neither from where it stands. So the
|
|
12
|
+
// instance answers the question about itself, in one call, and the deploy script
|
|
13
|
+
// decides what to do with the answer.
|
|
14
|
+
//
|
|
15
|
+
// IT DECIDES NOTHING AND HOLDS NOTHING. This route is a read: it reports the mode,
|
|
16
|
+
// the open reviews and the halt sentence that would be shown. Nothing about a ship,
|
|
17
|
+
// a land or a grade passes through here — ADR 0241's whole reconciliation with ADR
|
|
18
|
+
// 0162 is that the artist's authority attaches to the DEPLOY, and a read surface
|
|
19
|
+
// that could reach the ship path would smuggle that back in.
|
|
20
|
+
//
|
|
21
|
+
// MODULE BOUNDARY: reaches core only through the published doorway
|
|
22
|
+
// `../../../src/module-api` (which carries the one artist-gate reader); `../db` is
|
|
23
|
+
// this module's own data layer.
|
|
24
|
+
|
|
25
|
+
const express = require('express');
|
|
26
|
+
const api = require('../../../src/module-api');
|
|
27
|
+
const db = require('../db');
|
|
28
|
+
|
|
29
|
+
const log = api.logger('lifecycle');
|
|
30
|
+
|
|
31
|
+
module.exports = function buildArtistGateRouter() {
|
|
32
|
+
const router = express.Router();
|
|
33
|
+
|
|
34
|
+
// GET /artist-gate — this project's artist strictness and what it is holding.
|
|
35
|
+
//
|
|
36
|
+
// rank: any authenticated builder. The payload is ordinary board state — a
|
|
37
|
+
// policy value the instance already publishes as its own behaviour, and task
|
|
38
|
+
// ids/titles any builder can read on the hall — so a floor above requireBuilder
|
|
39
|
+
// would gate a fact the board already shows. Nothing here is writable.
|
|
40
|
+
//
|
|
41
|
+
// Response:
|
|
42
|
+
// mode 'off' | 'advisory' | 'strict' the project's setting as resolved
|
|
43
|
+
// since ISO string | null when it was last RAISED to strict
|
|
44
|
+
// holds boolean does anything hold a deploy now?
|
|
45
|
+
// truncated boolean `reviews` hit the scan cap — a floor
|
|
46
|
+
// reviews [{ id, created_at }] the open reviews that count
|
|
47
|
+
// halt { code, message, detail } | null the refusal, pre-composed
|
|
48
|
+
//
|
|
49
|
+
// `halt` is composed HERE rather than by the caller on purpose: ADR 0241 §4's
|
|
50
|
+
// second guard is that the refusal must name what holds the deploy and which
|
|
51
|
+
// control releases it, and a sentence assembled independently by each caller is
|
|
52
|
+
// a sentence that eventually says something different. One composer, one wording.
|
|
53
|
+
router.get('/artist-gate', api.requireBuilder, async (req, res) => {
|
|
54
|
+
try {
|
|
55
|
+
const gate = api.artistGate;
|
|
56
|
+
const mode = gate.artistGateMode();
|
|
57
|
+
const since = gate.artistGateSince();
|
|
58
|
+
|
|
59
|
+
// At 'off' no reviews are filed, so there is nothing to read and nothing to
|
|
60
|
+
// hold — answer without touching the database.
|
|
61
|
+
const scan = mode === 'off' ? { reviews: [], truncated: false } : await db.listOpenArtistReviews({
|
|
62
|
+
source: gate.ARTIST_REVIEW_SOURCE,
|
|
63
|
+
refPrefix: gate.ARTIST_REVIEW_REF_PREFIX,
|
|
64
|
+
resolvedStatuses: gate.RESOLVED_REVIEW_STATUSES,
|
|
65
|
+
});
|
|
66
|
+
const open = gate.unresolvedArtistReviews(scan.reviews, { since });
|
|
67
|
+
// `truncated` only survives if the cutoff did not itself empty the page:
|
|
68
|
+
// reporting "200+" over a list the non-retroactivity filter cut to three
|
|
69
|
+
// would be a worse lie than the number it replaced.
|
|
70
|
+
const truncated = scan.truncated && open.length === scan.reviews.length;
|
|
71
|
+
const halt = gate.artistGateHalt({ mode, reviews: open, truncated });
|
|
72
|
+
|
|
73
|
+
// A gate read is a policy comparison — a cached one is wrong the moment a
|
|
74
|
+
// review is dismissed (the project-settings / rot.js precedent).
|
|
75
|
+
res.set('Cache-Control', 'no-store');
|
|
76
|
+
res.json({
|
|
77
|
+
ok: true,
|
|
78
|
+
mode,
|
|
79
|
+
since,
|
|
80
|
+
holds: !!halt,
|
|
81
|
+
// A floor, not a census, when the scan hit its cap — said out loud so a
|
|
82
|
+
// caller never reads this list as "and that is all of them".
|
|
83
|
+
truncated,
|
|
84
|
+
// IDS AND DATES ONLY — deliberately no TITLE. A title is builder-written
|
|
85
|
+
// board text, and it was the only untrusted string this payload carried;
|
|
86
|
+
// nothing consumes it (go-live names ids, and a reader who wants the title
|
|
87
|
+
// has the board). Carrying it would have left a value that is safe as JSON
|
|
88
|
+
// today and becomes an injection vector the first time some surface renders
|
|
89
|
+
// it as HTML. Dropping it is the fix rather than escaping it, because an
|
|
90
|
+
// HTML-escaped value in a JSON API is wrong for every correct consumer.
|
|
91
|
+
reviews: open.map((t) => ({ id: String(t.id), created_at: t.created_at })),
|
|
92
|
+
halt,
|
|
93
|
+
});
|
|
94
|
+
} catch (err) {
|
|
95
|
+
log.error('[lifecycle] GET /artist-gate', err);
|
|
96
|
+
res.fail('artist_gate_read_failed', { status: 500, message: 'internal error' });
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return router;
|
|
101
|
+
};
|
|
@@ -85,9 +85,21 @@ function taskNotReadyHint(taskId, currentStatus) {
|
|
|
85
85
|
// Ordering is the fix: the universally-available command leads, /merge-mode is
|
|
86
86
|
// marked as needing a push credential rather than dropped (it is still the right
|
|
87
87
|
// tool on a credentialed machine), and /builder-release stays as the give-up exit.
|
|
88
|
+
//
|
|
89
|
+
// AND THE VERB CHANGED (task 1002788). It used to say "rebase the branch and
|
|
90
|
+
// re-ship", which is advice that can DEADLOCK the builder who follows it. The
|
|
91
|
+
// branch is already published; server-mediated publish is append-only by
|
|
92
|
+
// deliberate design (github-push.js: "Refuse non-fast-forward implicitly (no
|
|
93
|
+
// --force, ever)"), so a rebase rewrites history the server already holds and
|
|
94
|
+
// every subsequent publish is rejected non-fast-forward. The builder is then
|
|
95
|
+
// gated from new claims AND unable to land the one they hold. Reproduced
|
|
96
|
+
// end-to-end on task 1002485. `git merge origin/main` reaches the same
|
|
97
|
+
// up-to-date branch and stays a fast-forward, which is why the recovery text in
|
|
98
|
+
// ship-land.js already names merge and explicitly says "Do NOT reset or
|
|
99
|
+
// re-rebase". This is the same advice, given BEFORE the deadlock instead of after.
|
|
88
100
|
function rebaseRequiredHint(rebaseTasks) {
|
|
89
101
|
const ids = (rebaseTasks || []).map((t) => `task ${t.id}`);
|
|
90
|
-
return `Your queue is gated: ${ids.length} confirmed task(s)
|
|
102
|
+
return `Your queue is gated: ${ids.length} confirmed task(s) are waiting to land (${ids.join(', ') || 'unknown'}). Clear it with /strand-fix N (works on any machine), or bring the branch up to date with \`git merge origin/main\` and re-ship — MERGE, never rebase: server-mediated publish is append-only, and rewriting a published branch makes it un-pushable (task 1002788). /merge-mode only if this machine has a GitHub push credential. To give up on one instead, /builder-release it. If CI is simply backed up, waiting is correct: a task already landed clears the gate on its own.`;
|
|
91
103
|
}
|
|
92
104
|
|
|
93
105
|
// task 1642 (+ the worktree-keying fix) — the pure one-active-claim decision.
|
|
@@ -54,6 +54,12 @@ function registerSeams() {
|
|
|
54
54
|
getTask: (id) => db.getTask(id),
|
|
55
55
|
findTaskBySourceRef: (ref) => db.findTaskBySourceRef(ref),
|
|
56
56
|
createTask: (spec) => db.createTask(spec),
|
|
57
|
+
// The project's artist gate (ADR 0241, task 1003575). Resolved through the
|
|
58
|
+
// doorway's one reader rather than from branding().project, so the level that
|
|
59
|
+
// decides whether a review is FILED and the level that decides whether one
|
|
60
|
+
// HOLDS a deploy can never be read differently. At 'off' the artist-review
|
|
61
|
+
// rule files nothing.
|
|
62
|
+
artistGateMode: () => api.artistGate.artistGateMode(),
|
|
57
63
|
log: api.logger ? api.logger('lifecycle') : null,
|
|
58
64
|
}));
|
|
59
65
|
}
|
|
@@ -26,6 +26,7 @@ const { classifyTaskKind, VALID_KINDS, VALID_DISCIPLINES, VOTABLE_TASK_STATUSES,
|
|
|
26
26
|
const { isModuleKey, moduleKeys } = api.moduleScopeMap;
|
|
27
27
|
const { validateOrRespond, LIMITS, parseId } = api;
|
|
28
28
|
const { badKind, detectsMigration } = require('./task-route-helpers.js');
|
|
29
|
+
const { describeCheckViolation } = require('../pg-constraint-fail.js');
|
|
29
30
|
const { goalNotOpenRefusal } = require('./goal-route-authz.js');
|
|
30
31
|
// task 1003208: structured logging (pino via the doorway) — was console.*.
|
|
31
32
|
const log = api.logger('lifecycle');
|
|
@@ -275,6 +276,12 @@ module.exports = function registerTaskWriteRoutes(router) {
|
|
|
275
276
|
});
|
|
276
277
|
} catch (err) {
|
|
277
278
|
log.error('[gds] POST /tasks', err);
|
|
279
|
+
// task 1002845: a CHECK violation is a CLIENT-correctable problem wearing a
|
|
280
|
+
// 500. Name the constraint so the next validator/DB range mismatch is
|
|
281
|
+
// self-diagnosing instead of costing another round of blind retries. The
|
|
282
|
+
// constraint name only — err.detail carries the failing row's values.
|
|
283
|
+
const cv = describeCheckViolation(err);
|
|
284
|
+
if (cv) return res.fail(cv.code, { status: cv.status, message: cv.message, details: cv.details });
|
|
278
285
|
res.fail('create_failed', { status: 500, message: 'internal error' });
|
|
279
286
|
}
|
|
280
287
|
});
|
|
@@ -997,6 +997,12 @@ module.exports = function buildTasksRouter() {
|
|
|
997
997
|
// asks an Archon to flip the task to confirmed anyway. Audit-logged via the
|
|
998
998
|
// global write-audit middleware. Open requests surface in the Archon queue.
|
|
999
999
|
// rank: any-builder — opens a request; the decision still rests with Archon.
|
|
1000
|
+
//
|
|
1001
|
+
// The SAME route also opens the artist gate's release (ADR 0241 §3, task
|
|
1002
|
+
// 1003575): a request whose target is an unresolved artist review is admitted
|
|
1003
|
+
// at ANY status, because such a review sits at `backlog` and a completed-only
|
|
1004
|
+
// check would make the one documented escape hatch unusable for the exact case
|
|
1005
|
+
// it was written for. Who may DECIDE it is narrower — see the decide route.
|
|
1000
1006
|
router.post('/tasks/:id/override-request', auth.requireBuilder, async (req, res) => {
|
|
1001
1007
|
const id = parseId(req, res, { code: 'bad_task_id' });
|
|
1002
1008
|
if (id === null) return;
|
|
@@ -1328,6 +1334,14 @@ module.exports = function buildTasksRouter() {
|
|
|
1328
1334
|
// updates; the task stays at completed and the builder must release or
|
|
1329
1335
|
// re-ship after fixing whatever the grader caught.
|
|
1330
1336
|
// rank: archon — grader-bypass authority; same gate as POST /tasks/:id/confirm.
|
|
1337
|
+
//
|
|
1338
|
+
// ONE EXCEPTION, and it is NARROWER than this gate rather than wider: a request
|
|
1339
|
+
// whose target is an unresolved ARTIST REVIEW is a gate release (ADR 0241 §3,
|
|
1340
|
+
// db-overrides.js), and only the PROJECT OWNER may decide it. That check lives
|
|
1341
|
+
// in decideOverrideRequest, not here, so it cannot be bypassed by a second
|
|
1342
|
+
// caller; this route only renders its refusal (403 override_owner_only).
|
|
1343
|
+
// Approving one confirms nothing and pays nothing — it stops the review holding
|
|
1344
|
+
// the DEPLOY and leaves it open on the board.
|
|
1331
1345
|
router.post('/override-requests/:id/decide', auth.requireBuilder, auth.requirePermission('override_request.decide'), async (req, res) => {
|
|
1332
1346
|
const id = parseId(req, res, { code: 'bad_request_id' });
|
|
1333
1347
|
if (id === null) return;
|
|
@@ -1351,6 +1365,16 @@ module.exports = function buildTasksRouter() {
|
|
|
1351
1365
|
if (err.code === 'REQUEST_NOT_OPEN') {
|
|
1352
1366
|
return res.fail('request_not_open', 409);
|
|
1353
1367
|
}
|
|
1368
|
+
if (err.code === 'OVERRIDE_OWNER_ONLY') {
|
|
1369
|
+
// The artist gate's release is narrower than this route's Archon floor
|
|
1370
|
+
// (ADR 0241 §3): the PROJECT OWNER decides it, nobody else. 403 with the
|
|
1371
|
+
// reason spelled out — an Archon refused here is not a bug, and a refusal
|
|
1372
|
+
// that does not say which authority is missing sends them hunting.
|
|
1373
|
+
return res.fail('override_owner_only', {
|
|
1374
|
+
status: 403,
|
|
1375
|
+
message: 'Releasing a deploy held by an artist review is the project owner\'s call alone — not an Archon\'s (ADR 0241). Resolve or dismiss the review instead, or ask the owner to decide this request.',
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
1354
1378
|
if (err.code === 'OVERRIDE_NO_REQUESTER') {
|
|
1355
1379
|
// Data-integrity guard (#538): an approved override with no requester to
|
|
1356
1380
|
// credit. Should never happen (NOT NULL column); refuse rather than
|
|
@@ -173,9 +173,18 @@ const VALID_DISCIPLINES = new Set(['engineer', 'artist', 'ideator', 'ui', 'uncla
|
|
|
173
173
|
// `create_failed` constraint-violation message, and taught the context pack the
|
|
174
174
|
// wrong range — task 1233.) `convention` is retained so the pack can surface the
|
|
175
175
|
// human-facing "use P1–P5 tiers" label alongside the accepted range.
|
|
176
|
+
// task 1002845: `manual_degree` had the SAME defect priority had, and is fixed the
|
|
177
|
+
// same way — tighten the validator to the DB, never migrate the DB to the
|
|
178
|
+
// validator. The CHECK is `manual_degree BETWEEN 1 AND 5`
|
|
179
|
+
// (migrations/003_pms.sql, never widened since), so min:0 let a 0 pass the route
|
|
180
|
+
// and then die in the INSERT as a 23514, which the generic catch answered as an
|
|
181
|
+
// opaque 500 `create_failed` with the constraint name never reaching the caller.
|
|
182
|
+
// Measured against the live API before the fix: POST /tasks {manual_degree:0}
|
|
183
|
+
// → 500 "internal error". Idea 1000456 records roughly four blind retries spent
|
|
184
|
+
// on this during task 1604.
|
|
176
185
|
const TASK_FIELD_BOUNDS = {
|
|
177
186
|
priority: { min: 1, max: 5, convention: { min: 1, max: 5, label: 'P1–P5 planning tiers' } },
|
|
178
|
-
manual_degree: { min:
|
|
187
|
+
manual_degree: { min: 1, max: 5 },
|
|
179
188
|
};
|
|
180
189
|
|
|
181
190
|
// Task statuses that can receive peer-acclaim votes (a work must be at least
|
|
@@ -250,5 +250,48 @@ await test('the review inherits the shipped task goal and version, not goal 1000
|
|
|
250
250
|
assert.equal(matched[0].spec.versionId, 'BONGOS-V1');
|
|
251
251
|
});
|
|
252
252
|
|
|
253
|
+
// --- the project's artist gate decides whether it is filed at all (ADR 0241) --
|
|
254
|
+
|
|
255
|
+
await test("artist_gate 'off' files NO artist review — the one level at which the rule is silent", () => {
|
|
256
|
+
assert.equal(cascade.cascadesFor(shippedCopy(), cascade.CASCADES, { artistGate: 'off' }).matched.length, 0);
|
|
257
|
+
// The other two levels both FILE; what separates them is whether an open review
|
|
258
|
+
// holds a deploy, which is not this file's business at all.
|
|
259
|
+
assert.equal(cascade.cascadesFor(shippedCopy(), cascade.CASCADES, { artistGate: 'advisory' }).matched.length, 1);
|
|
260
|
+
assert.equal(cascade.cascadesFor(shippedCopy(), cascade.CASCADES, { artistGate: 'strict' }).matched.length, 1);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
await test('an UNREAD policy files — silence is never read as an opt-out', () => {
|
|
264
|
+
// No context at all (an old caller), and an explicitly-undefined level (a reader
|
|
265
|
+
// that threw) must both behave like the gate's default, which is 'strict'.
|
|
266
|
+
assert.equal(cascade.cascadesFor(shippedCopy()).matched.length, 1);
|
|
267
|
+
assert.equal(cascade.cascadesFor(shippedCopy(), cascade.CASCADES, {}).matched.length, 1);
|
|
268
|
+
assert.equal(cascade.cascadesFor(shippedCopy(), cascade.CASCADES, { artistGate: undefined }).matched.length, 1);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
await test('a gate reader that THROWS costs the run nothing — it is reported, and the review is still filed', async () => {
|
|
272
|
+
const created = [];
|
|
273
|
+
const report = await cascade.runCascadesForShip({ id: '7001' }, {
|
|
274
|
+
getTask: async () => shippedCopy(),
|
|
275
|
+
findTaskBySourceRef: async () => null,
|
|
276
|
+
createTask: async (spec) => { created.push(spec); return { id: '7002' }; },
|
|
277
|
+
artistGateMode: () => { throw new Error('pack unreadable'); },
|
|
278
|
+
});
|
|
279
|
+
assert.equal(created.length, 1, 'an unreadable policy must not silently behave like off');
|
|
280
|
+
assert.ok(report.errors.some((e) => /artistGateMode\(\) threw/.test(e)), 'and it says so');
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
await test("runCascadesForShip honours 'off' end to end — nothing is created, nothing errors", async () => {
|
|
284
|
+
let creates = 0;
|
|
285
|
+
const report = await cascade.runCascadesForShip({ id: '7001' }, {
|
|
286
|
+
getTask: async () => shippedCopy(),
|
|
287
|
+
findTaskBySourceRef: async () => null,
|
|
288
|
+
createTask: async () => { creates++; return { id: '7002' }; },
|
|
289
|
+
artistGateMode: () => 'off',
|
|
290
|
+
});
|
|
291
|
+
assert.equal(creates, 0);
|
|
292
|
+
assert.deepEqual(report.created, []);
|
|
293
|
+
assert.deepEqual(report.errors, [], 'a project that switched the gate off has no problem to report');
|
|
294
|
+
});
|
|
295
|
+
|
|
253
296
|
console.log(`\n${passed} passed, ${failed} failed`);
|
|
254
297
|
process.exit(failed ? 1 : 0);
|
|
@@ -159,17 +159,34 @@ const RESTART_SHAPES = new Set(['co-tenant', 'standalone']);
|
|
|
159
159
|
// which is why it is the default — DEFAULT_SETTINGS must be today's behaviour, and
|
|
160
160
|
// today a public project runs whatever door its joinability runs. Composed at the
|
|
161
161
|
// door by src/bongos/project-door.js; ADR 0247.
|
|
162
|
+
//
|
|
163
|
+
// `artist_gate` is the project's ARTIST STRICTNESS (ADR 0241, task 1003575) — the
|
|
164
|
+
// owner's constraint from goal 1000074, verbatim: blocking strictness is per
|
|
165
|
+
// project, not global. `off` generates no artist review tasks at all; `advisory`
|
|
166
|
+
// generates them and blocks nothing; `strict` generates them and an unresolved one
|
|
167
|
+
// holds the DEPLOY — the instance's pin may not move while one is open. It never
|
|
168
|
+
// holds a builder's SHIP: that is the line ADR 0241 draws to keep this compatible
|
|
169
|
+
// with ADR 0162, and it is why a gate that stops work can exist here at all.
|
|
170
|
+
//
|
|
171
|
+
// It is the only key here whose default is NOT today's behaviour. `strict` is an
|
|
172
|
+
// owner ruling (ADR 0241 §4), recorded there against its author's recommendation
|
|
173
|
+
// and coherent because strict holds only the pin: the worst case is a project whose
|
|
174
|
+
// deploy waits on a review nobody has done, which is visible and owner-releasable,
|
|
175
|
+
// never a stranded builder.
|
|
162
176
|
const SETTINGS_VOCAB = Object.freeze({
|
|
163
177
|
platform_visibility: ['public', 'gated'],
|
|
164
178
|
joinability: ['open', 'apply', 'invite_only'],
|
|
165
179
|
visibility: ['public', 'private', 'stealth'],
|
|
166
180
|
join_grant: ['view', 'apply', 'full'],
|
|
181
|
+
artist_gate: ['off', 'advisory', 'strict'],
|
|
167
182
|
});
|
|
168
183
|
// Default = today's behavior on every axis, including the join door: ADR 0182's
|
|
169
184
|
// migration gives the hub column the same `public` default, "existing rows keep
|
|
170
|
-
// today's behaviour".
|
|
185
|
+
// today's behaviour". The one exception is `artist_gate`, whose default is the
|
|
186
|
+
// owner's ruling rather than the status quo — see the note above it.
|
|
171
187
|
const DEFAULT_SETTINGS = Object.freeze({
|
|
172
188
|
platform_visibility: 'public', joinability: 'apply', visibility: 'public', join_grant: 'full',
|
|
189
|
+
artist_gate: 'strict',
|
|
173
190
|
});
|
|
174
191
|
|
|
175
192
|
// The project's PLANET (task 1003347, direction record §16): how the platform
|
|
@@ -422,9 +439,28 @@ function publicGithubApp(manifestRow) {
|
|
|
422
439
|
// and the live push (the runner's settings-apply leg patches exactly these lines).
|
|
423
440
|
function settingsEnvVars(row, prefix) {
|
|
424
441
|
const s = effectiveSettings(row);
|
|
425
|
-
|
|
442
|
+
const vars = Object.fromEntries(Object.keys(SETTINGS_VOCAB).map((key) => [`${prefix}_${key.toUpperCase()}`, s[key]]));
|
|
443
|
+
for (const [suffix, read] of Object.entries(SETTINGS_COMPANION_ENV)) vars[`${prefix}_${suffix}`] = read(row);
|
|
444
|
+
return vars;
|
|
426
445
|
}
|
|
427
446
|
|
|
447
|
+
// COMPANION variables: env the instance needs to OBEY a setting, which is not
|
|
448
|
+
// itself a setting anyone chooses (task 1003575). Declared as a closed table for
|
|
449
|
+
// the same reason SETTINGS_VOCAB is — the drift test asserts the transport carries
|
|
450
|
+
// exactly the vocabulary plus these, so a companion can be added deliberately and
|
|
451
|
+
// never by accident.
|
|
452
|
+
//
|
|
453
|
+
// ARTIST_GATE_SINCE is the only one today: ADR 0241 §5 makes a tightening
|
|
454
|
+
// non-retroactive, and an instance cannot honour that without knowing WHEN the
|
|
455
|
+
// gate was raised. Always emitted — the runner patches these lines in place, and a
|
|
456
|
+
// key that came and went would leave a stale line behind — with empty meaning "no
|
|
457
|
+
// cutoff". src/branding.js skips an empty override, so the instance reads no
|
|
458
|
+
// cutoff and every unresolved review counts, which is the right answer for a
|
|
459
|
+
// project that has been strict since it stood up.
|
|
460
|
+
const SETTINGS_COMPANION_ENV = Object.freeze({
|
|
461
|
+
ARTIST_GATE_SINCE: (row) => artistGateStrictSince(row) || '',
|
|
462
|
+
});
|
|
463
|
+
|
|
428
464
|
// The web.env block the runner writes at STANDUP (task 1003139): a comment that
|
|
429
465
|
// tells the truth about where the values come from, then the settingsEnvVars
|
|
430
466
|
// lines. Changing one on an ALREADY-RUNNING instance is the 'settings-apply'
|
|
@@ -496,6 +532,44 @@ function settingsPushOwed(row) {
|
|
|
496
532
|
return stored[SETTINGS_PUSH_OWED_KEY] === true;
|
|
497
533
|
}
|
|
498
534
|
|
|
535
|
+
// The moment `artist_gate` was last RAISED to 'strict' (ADR 0241 §5, task
|
|
536
|
+
// 1003575). Same reserved-key shape and rationale as SETTINGS_PUSH_OWED_KEY
|
|
537
|
+
// above — platform bookkeeping, not a setting an owner declares, so it is
|
|
538
|
+
// deliberately out of SETTINGS_VOCAB and can neither leak through
|
|
539
|
+
// effectiveSettings nor be written by a client patch.
|
|
540
|
+
//
|
|
541
|
+
// WHY IT HAS TO EXIST. ADR 0241 §5: tightening applies to work that ships AFTER
|
|
542
|
+
// the change, never backwards. Without a stamp, flipping advisory → strict would
|
|
543
|
+
// hand the next deploy a backlog of review tasks for work nobody remembers — the
|
|
544
|
+
// exact thing that makes a setting frightening to raise. The stamp is what lets
|
|
545
|
+
// the gate ignore them.
|
|
546
|
+
const ARTIST_GATE_STRICT_SINCE_KEY = 'artist_gate_strict_since';
|
|
547
|
+
|
|
548
|
+
// Read the stamp, or null. An unparseable value reads as null (no cutoff) rather
|
|
549
|
+
// than as a date nobody can name. PURE.
|
|
550
|
+
function artistGateStrictSince(row) {
|
|
551
|
+
const stored = row && row.settings && typeof row.settings === 'object' && !Array.isArray(row.settings)
|
|
552
|
+
? row.settings : {};
|
|
553
|
+
const v = stored[ARTIST_GATE_STRICT_SINCE_KEY];
|
|
554
|
+
if (typeof v !== 'string' || !v) return null;
|
|
555
|
+
return Number.isNaN(Date.parse(v)) ? null : v;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// The extra jsonb keys a settings save must ALSO store, given what the row holds
|
|
559
|
+
// now and what is being patched onto it. Today that is exactly the strict stamp,
|
|
560
|
+
// and it is written only on a RAISE — a save that leaves the gate where it was,
|
|
561
|
+
// or lowers it, must not move the cutoff forward, or every unrelated settings
|
|
562
|
+
// change would quietly re-date the gate and un-hold reviews it was already
|
|
563
|
+
// holding. Lowering deliberately LEAVES the stamp: nothing reads it below
|
|
564
|
+
// 'strict', and keeping it means a project that goes strict → advisory → strict
|
|
565
|
+
// by mistake does not lose its history. PURE (row + patch + clock in, object out).
|
|
566
|
+
function artistGateStamp(row, patch, now = new Date()) {
|
|
567
|
+
const next = (patch || {}).artist_gate;
|
|
568
|
+
if (next !== 'strict') return {};
|
|
569
|
+
if (effectiveSettings(row).artist_gate === 'strict') return {};
|
|
570
|
+
return { [ARTIST_GATE_STRICT_SINCE_KEY]: new Date(now).toISOString() };
|
|
571
|
+
}
|
|
572
|
+
|
|
499
573
|
// A description as it may be STORED: trimmed, collapsed of runaway whitespace,
|
|
500
574
|
// capped, and empty-means-absent. Returns null for anything that isn't real prose,
|
|
501
575
|
// because null is what leaves the project dark-matter — an empty string stored in a
|
|
@@ -1334,12 +1408,14 @@ module.exports = {
|
|
|
1334
1408
|
SETTINGS_APPLY_FROM, SETTINGS_APPLY_SHAPES,
|
|
1335
1409
|
RESTART_FROM, RESTART_SHAPES,
|
|
1336
1410
|
TIER_MONTHLY_USD, CERT_ELIGIBLE_STATES,
|
|
1337
|
-
SETTINGS_VOCAB, DEFAULT_SETTINGS, PROJECT_DETAIL_VOCAB, DESCRIPTION_MAX,
|
|
1411
|
+
SETTINGS_VOCAB, DEFAULT_SETTINGS, SETTINGS_COMPANION_ENV, PROJECT_DETAIL_VOCAB, DESCRIPTION_MAX,
|
|
1338
1412
|
PLANET_TEMPLATES, PLANET_ACCENTS, PLANET_VOCAB,
|
|
1339
1413
|
// pure
|
|
1340
1414
|
isValidSlug, costEstimateUsd, publicInstance, normalizeHostname,
|
|
1341
1415
|
defaultDomainForSlug, isReservedHostname, effectiveSettings, planetChoice,
|
|
1342
1416
|
settingsPushOwed,
|
|
1417
|
+
// pure — the artist gate's non-retroactivity stamp (ADR 0241 §5, task 1003575)
|
|
1418
|
+
ARTIST_GATE_STRICT_SINCE_KEY, artistGateStrictSince, artistGateStamp,
|
|
1343
1419
|
normalizeDescription, normalizeProjectDetail, projectDetailPatch, projectDetail,
|
|
1344
1420
|
// pure — GitHub App Manifest flow (task 2080)
|
|
1345
1421
|
githubAppName, buildGithubAppManifest, manifestPostUrl, publicGithubApp,
|
|
@@ -908,7 +908,12 @@ module.exports = function provisioningRoutes() {
|
|
|
908
908
|
// true: the re-send is unchanged, so it would otherwise hit "No change".
|
|
909
909
|
const pushOwed = provisioning.settingsPushOwed(inst);
|
|
910
910
|
const policyResent = (!!inst.error_note || pushOwed) && policyTouched;
|
|
911
|
-
|
|
911
|
+
// Raising `artist_gate` to 'strict' also stamps WHEN (ADR 0241 §5 makes the
|
|
912
|
+
// tightening non-retroactive, and the instance cannot honour that without the
|
|
913
|
+
// date). It rides the same single UPDATE as the setting it belongs to, and is
|
|
914
|
+
// empty on every other save — see artistGateStamp.
|
|
915
|
+
const storedPatch = { ...patch, ...provisioning.artistGateStamp(inst, patch) };
|
|
916
|
+
const updated = changed ? await provisioning.updateInstanceSettings(pool, id, storedPatch) : inst;
|
|
912
917
|
if (changed) {
|
|
913
918
|
await provisioning.recordEvent(pool, {
|
|
914
919
|
instanceId: id, ownerBuilderId: inst.owner_builder_id, event: 'settings',
|
|
@@ -263,27 +263,27 @@ await ta('recordDnsNote / clearErrorNote are NARROW single-column updates — no
|
|
|
263
263
|
console.log('\nPer-project settings (task 1003040):');
|
|
264
264
|
|
|
265
265
|
t('effectiveSettings: defaults for a bare/empty row = today\'s behavior; stored keys overlay; junk falls back', () => {
|
|
266
|
-
assert.deepEqual(P.effectiveSettings(null), { platform_visibility: 'public', joinability: 'apply', visibility: 'public', join_grant: 'full' });
|
|
267
|
-
assert.deepEqual(P.effectiveSettings({}), { platform_visibility: 'public', joinability: 'apply', visibility: 'public', join_grant: 'full' });
|
|
268
|
-
assert.deepEqual(P.effectiveSettings({ settings: {} }), { platform_visibility: 'public', joinability: 'apply', visibility: 'public', join_grant: 'full' });
|
|
266
|
+
assert.deepEqual(P.effectiveSettings(null), { platform_visibility: 'public', joinability: 'apply', visibility: 'public', join_grant: 'full', artist_gate: 'strict' });
|
|
267
|
+
assert.deepEqual(P.effectiveSettings({}), { platform_visibility: 'public', joinability: 'apply', visibility: 'public', join_grant: 'full', artist_gate: 'strict' });
|
|
268
|
+
assert.deepEqual(P.effectiveSettings({ settings: {} }), { platform_visibility: 'public', joinability: 'apply', visibility: 'public', join_grant: 'full', artist_gate: 'strict' });
|
|
269
269
|
assert.deepEqual(
|
|
270
270
|
P.effectiveSettings({ settings: { platform_visibility: 'gated' } }),
|
|
271
|
-
{ platform_visibility: 'gated', joinability: 'apply', visibility: 'public', join_grant: 'full' },
|
|
271
|
+
{ platform_visibility: 'gated', joinability: 'apply', visibility: 'public', join_grant: 'full', artist_gate: 'strict' },
|
|
272
272
|
'a stored key overlays only itself'
|
|
273
273
|
);
|
|
274
274
|
assert.deepEqual(
|
|
275
275
|
P.effectiveSettings({ settings: { platform_visibility: 'bogus', joinability: 'invite_only', future_key: 'x', visibility: 'unlisted', join_grant: 'everything' } }),
|
|
276
|
-
{ platform_visibility: 'public', joinability: 'invite_only', visibility: 'public', join_grant: 'full' },
|
|
276
|
+
{ platform_visibility: 'public', joinability: 'invite_only', visibility: 'public', join_grant: 'full', artist_gate: 'strict' },
|
|
277
277
|
'off-vocabulary values fall back to the default; unknown keys never leak out'
|
|
278
278
|
);
|
|
279
279
|
assert.deepEqual(P.effectiveSettings({ settings: ['not', 'an', 'object'] }),
|
|
280
|
-
{ platform_visibility: 'public', joinability: 'apply', visibility: 'public', join_grant: 'full' }, 'a non-object settings value reads as defaults');
|
|
280
|
+
{ platform_visibility: 'public', joinability: 'apply', visibility: 'public', join_grant: 'full', artist_gate: 'strict' }, 'a non-object settings value reads as defaults');
|
|
281
281
|
// the join door reads back like any other policy key (task 1002324)
|
|
282
282
|
assert.deepEqual(P.effectiveSettings({ settings: { visibility: 'stealth' } }),
|
|
283
|
-
{ platform_visibility: 'public', joinability: 'apply', visibility: 'stealth', join_grant: 'full' }, 'a stored door overlays only itself');
|
|
283
|
+
{ platform_visibility: 'public', joinability: 'apply', visibility: 'stealth', join_grant: 'full', artist_gate: 'strict' }, 'a stored door overlays only itself');
|
|
284
284
|
// what a public join grants is a policy key like any other (task 1002331)
|
|
285
285
|
assert.deepEqual(P.effectiveSettings({ settings: { join_grant: 'view' } }),
|
|
286
|
-
{ platform_visibility: 'public', joinability: 'apply', visibility: 'public', join_grant: 'view' }, 'the grant overlays only itself');
|
|
286
|
+
{ platform_visibility: 'public', joinability: 'apply', visibility: 'public', join_grant: 'view', artist_gate: 'strict' }, 'the grant overlays only itself');
|
|
287
287
|
});
|
|
288
288
|
|
|
289
289
|
t('SETTINGS_VOCAB / DEFAULT_SETTINGS: every default is on its own vocabulary (lockstep guard)', () => {
|
|
@@ -132,11 +132,47 @@ function looksHighEntropySecret(tok) {
|
|
|
132
132
|
// floor and slip the entropy net — but layers 1–4 (literal env values, PEM,
|
|
133
133
|
// known shapes, key=value assignments) already cover the realistic ways such a
|
|
134
134
|
// value reaches a transcript.
|
|
135
|
+
const DOC_EXTS = ['.md', '.sql'];
|
|
135
136
|
const ENTROPY_CANDIDATE_RE = /[A-Za-z0-9_-]{20,}/g;
|
|
136
|
-
|
|
137
|
-
|
|
137
|
+
|
|
138
|
+
// DOC-PATH EXEMPTION (task 1002460) — an ALLOWLIST, not a shape guess.
|
|
139
|
+
//
|
|
140
|
+
// THE FALSE POSITIVE. A long filename slug is lowercase + digits + hyphens with
|
|
141
|
+
// H >= 3.7, so rule B fires and package-core collapsed every ADR and migration
|
|
142
|
+
// filename to `<redacted>.md` on the publish path.
|
|
143
|
+
//
|
|
144
|
+
// WHY NOT A SHAPE RULE. Three were tried and each leaked. Any predicate built on
|
|
145
|
+
// what a filename LOOKS like also admits a secret dressed that way, because a
|
|
146
|
+
// random lowercase run is shape-identical to words — and entropy cannot separate
|
|
147
|
+
// them either (real filenames measure up to H=4.35, random lowercase words 4.54).
|
|
148
|
+
// scrubSecrets feeds the PUBLIC mirror (scripts/gds/mirror-redact.js,
|
|
149
|
+
// scripts/public-mirror-export.js), so a shape hole is a smuggling channel.
|
|
150
|
+
//
|
|
151
|
+
// WHAT THIS DOES INSTEAD. The caller passes the set of filenames it is actually
|
|
152
|
+
// publishing; a candidate is spared only when the token plus its extension IS one
|
|
153
|
+
// of them. A secret is not a file in the tree, so it cannot be in the set.
|
|
154
|
+
//
|
|
155
|
+
// RECALL IS UNCHANGED BY CONSTRUCTION, and that is the point. With no set — every
|
|
156
|
+
// transcript scrub, every caller that does not opt in — there is no exemption at
|
|
157
|
+
// all and the verdict is byte-identical to before this task. Only the publish path,
|
|
158
|
+
// which enumerates the tree anyway, supplies one.
|
|
159
|
+
function redactHighEntropy(text, opts) {
|
|
160
|
+
const known = opts && opts.knownDocNames;
|
|
161
|
+
if (!known || !known.size) {
|
|
162
|
+
return text.replace(ENTROPY_CANDIDATE_RE, (tok) => (looksHighEntropySecret(tok) ? REDACTED : tok));
|
|
163
|
+
}
|
|
164
|
+
return text.replace(ENTROPY_CANDIDATE_RE, (tok, offset, whole) => {
|
|
165
|
+
if (!looksHighEntropySecret(tok)) return tok;
|
|
166
|
+
// Cheap boundary reads at a known offset — no slicing of the surrounding
|
|
167
|
+
// document, which runs over whole markdown files and whole transcripts.
|
|
168
|
+
for (const ext of DOC_EXTS) {
|
|
169
|
+
if (whole.startsWith(ext, offset + tok.length) && known.has(tok + ext)) return tok;
|
|
170
|
+
}
|
|
171
|
+
return REDACTED;
|
|
172
|
+
});
|
|
138
173
|
}
|
|
139
174
|
|
|
175
|
+
|
|
140
176
|
// ENV secret collection — literal secret VALUES from the process env, so they are
|
|
141
177
|
// redacted wherever they appear even with no recognizable shape (Apple/MAC_* CI
|
|
142
178
|
// secrets, a baked GDS_TOKEN, GITHUB_TOKEN, DO_API_TOKEN, …). Key match mirrors
|
|
@@ -164,7 +200,7 @@ function collectEnvSecrets(env = process.env) {
|
|
|
164
200
|
return out;
|
|
165
201
|
}
|
|
166
202
|
|
|
167
|
-
function scrubSecrets(text, extraSecrets = []) {
|
|
203
|
+
function scrubSecrets(text, extraSecrets = [], opts = undefined) {
|
|
168
204
|
if (typeof text !== 'string' || text.length === 0) return text;
|
|
169
205
|
let out = text;
|
|
170
206
|
// Literal dynamic secrets first (longest first so a substring can't shadow a
|
|
@@ -190,7 +226,7 @@ function scrubSecrets(text, extraSecrets = []) {
|
|
|
190
226
|
// Catch-all LAST: an unkeyed, unknown-shape, high-entropy token. The earlier
|
|
191
227
|
// layers have already collapsed known secrets to "<redacted>" (no candidate),
|
|
192
228
|
// so this only sweeps what survived.
|
|
193
|
-
out = redactHighEntropy(out);
|
|
229
|
+
out = redactHighEntropy(out, opts);
|
|
194
230
|
return out;
|
|
195
231
|
}
|
|
196
232
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# `modules/status-ui/public/` — the status dashboard ("The Watch")
|
|
2
2
|
|
|
3
|
-
These files are the source of `status.example.com`. The surface moved here from `public-status/` in **BV1.R84** when the status dashboard became a first-class module ([ADR 0093](../../../docs/adr
|
|
3
|
+
These files are the source of `status.example.com`. The surface moved here from `public-status/` in **BV1.R84** when the status dashboard became a first-class module ([ADR 0093](../../../docs/adr/0093-tranche-2-core-carve-sequence.md) §1); the loader's web-surface seam serves this `public/` dir on the `status.` host (see [`../CLAUDE.md`](../CLAUDE.md)). As of **#566 / [ADR 0029](../../../docs/adr/0029-status-page-outage-resilience.md)** they are **mirrored OFF-BOX** so the status page survives a droplet outage.
|
|
4
4
|
|
|
5
5
|
## Why off-box
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Design contract — neutral adapter interface
|
|
2
2
|
|
|
3
|
-
**Status:** Accepted · authored by [task 1377](https://example.com/builders#/task/1377) · governed by [ADR 0081](../../../docs/adr
|
|
3
|
+
**Status:** Accepted · authored by [task 1377](https://example.com/builders#/task/1377) · governed by [ADR 0081](../../../docs/adr/0081-tool-agnostic-design-layer.md) · lives in the `ui-design` module since [task 1003324](https://cloudbongos.com/builders#/task/1003324) ([ADR 0197](../../../docs/adr/0197-the-ui-design-module.md)) — the token files it names stay in `config/` as the host boundary
|
|
4
4
|
|
|
5
5
|
The repo is the single source of truth for design. Every design tool is a swappable adapter that reads from and writes to this contract — it does not own the source. This file is the contract every adapter must conform to. The minimal validator (`modules/ui-design/scripts/validate-design.js`; `node scripts/gds/validate-design.js` is a thin shim over it) enforces the machine-checkable parts.
|
|
6
6
|
|
|
@@ -96,7 +96,7 @@ Rules:
|
|
|
96
96
|
| Freeform canvas art | — | `assets/ui/<surface>/` | Asset |
|
|
97
97
|
| Pixel-art game tiles | see `art/` pipeline | — | Art pipeline (not this contract) |
|
|
98
98
|
|
|
99
|
-
Pixel-art game tiles (world tiles, characters, objects) are out of scope for this contract — they are governed by the `art/` pipeline and [ADR 0088](../../../docs/adr
|
|
99
|
+
Pixel-art game tiles (world tiles, characters, objects) are out of scope for this contract — they are governed by the `art/` pipeline and [ADR 0088](../../../docs/adr/0088-consistent-character-generative-animation.md).
|
|
100
100
|
|
|
101
101
|
---
|
|
102
102
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# `modules/ui-design/kit/` — look before you ship
|
|
2
2
|
|
|
3
|
-
The stub, the renderer/auditor, the interaction probes, the mock checker and the contrast probe any builder points at any surface of any instance ([task 1003320](https://cloudbongos.com/builders#/task/1003320), [ADR 0197](../../../docs/adr
|
|
3
|
+
The stub, the renderer/auditor, the interaction probes, the mock checker and the contrast probe any builder points at any surface of any instance ([task 1003320](https://cloudbongos.com/builders#/task/1003320), [ADR 0197](../../../docs/adr/0197-the-ui-design-module.md)). Plain Node; the browser is the system Edge or Chrome via `playwright-core` (a devDependency — nothing is downloaded).
|
|
4
4
|
|
|
5
5
|
```
|
|
6
6
|
node modules/ui-design/kit/serve.js # the stub (SURFACE, PREFIX, AUTH=1, CHILD=1, LIVE=…, FAIL=…, STYLE=<look>, STUB_PORT)
|
|
@@ -188,6 +188,7 @@ const STUB_VOCAB = {
|
|
|
188
188
|
joinability: ['open', 'apply', 'invite_only'],
|
|
189
189
|
visibility: ['public', 'private', 'stealth'],
|
|
190
190
|
join_grant: ['view', 'apply', 'full'],
|
|
191
|
+
artist_gate: ['off', 'advisory', 'strict'],
|
|
191
192
|
planet_template: null, // the template set is long; shape is checked, not membership
|
|
192
193
|
planet_accent: ['sea', 'terracotta', 'gold-soft', 'sun-lit', 'accent'],
|
|
193
194
|
card_backdrop: ['nebula', 'dust', 'deepfield', 'chrome'],
|