@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,289 @@
|
|
|
1
|
+
// tests/watch_roster_self_row.mjs — the reader's OWN row on the Watch roster,
|
|
2
|
+
// EXECUTED (task 1003720, re-filed from task 1002951).
|
|
3
|
+
//
|
|
4
|
+
// The bug, as the owner reported it from Discord #bugs: "I cannot press
|
|
5
|
+
// offboard". GET /builders/roster returns EVERY builder — the reader included —
|
|
6
|
+
// and the row painted a live rank <select> and a live Offboard pill on all of
|
|
7
|
+
// them. But both routes behind that row refuse a self-change categorically and
|
|
8
|
+
// always will:
|
|
9
|
+
//
|
|
10
|
+
// PATCH /builders/:id/rank → 403 cannot_change_own_rank
|
|
11
|
+
// PATCH /builders/:id/status → 403 cannot_change_own_status
|
|
12
|
+
//
|
|
13
|
+
// (src/bongos/routes/builders.js — the guard that stops the last Archon demoting
|
|
14
|
+
// or offboarding themselves and locking everyone out.) res.fail() defaults the
|
|
15
|
+
// envelope's message to the CODE, so pressing your own Offboard produced a toast
|
|
16
|
+
// reading "Could not change standing: cannot_change_own_status" and nothing else.
|
|
17
|
+
// From the owner's seat that is a button that does not work.
|
|
18
|
+
//
|
|
19
|
+
// The page now renders no control on that row and states why once under the
|
|
20
|
+
// table. These are assertions about what a human sees and clicks, so this runs
|
|
21
|
+
// the real watch.js inside `vm` against the real watch.html, with a makeLedger
|
|
22
|
+
// that actually calls rowHtml — a source-text grep cannot see which row got
|
|
23
|
+
// which cell, and the no-op ledger stub in tests/watch_applications_queue.mjs
|
|
24
|
+
// renders no roster rows at all.
|
|
25
|
+
//
|
|
26
|
+
// Run: node tests/watch_roster_self_row.mjs
|
|
27
|
+
|
|
28
|
+
import { strict as assert } from 'node:assert';
|
|
29
|
+
import fs from 'node:fs';
|
|
30
|
+
import path from 'node:path';
|
|
31
|
+
import vm from 'node:vm';
|
|
32
|
+
import { fileURLToPath } from 'node:url';
|
|
33
|
+
import { makeRunner, makeEl, parseInto } from './helpers.mjs';
|
|
34
|
+
|
|
35
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
36
|
+
const HALL = path.join(ROOT, 'modules/hall-ui/public');
|
|
37
|
+
const SRC = fs.readFileSync(path.join(HALL, 'watch.js'), 'utf8');
|
|
38
|
+
const PAGE = fs.readFileSync(path.join(HALL, 'watch.html'), 'utf8');
|
|
39
|
+
|
|
40
|
+
const { test, summary } = makeRunner();
|
|
41
|
+
|
|
42
|
+
const escapeHtml = (s) => String(s == null ? '' : s).replace(/[&<>"']/g,
|
|
43
|
+
(c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
|
|
44
|
+
|
|
45
|
+
const tick = () => new Promise((r) => setTimeout(r, 0));
|
|
46
|
+
async function settle() { for (let i = 0; i < 40; i++) await tick(); }
|
|
47
|
+
|
|
48
|
+
const ok = (data) => ({ status: 200, ok: true, data });
|
|
49
|
+
|
|
50
|
+
// The branding pack renames every rank (the neutral pack calls archon "Owner"),
|
|
51
|
+
// which is why a hardcoded rank word in page copy is banned. This stub renames
|
|
52
|
+
// them too, so a test for "the foot line does not spell Archon" is real.
|
|
53
|
+
const RANK_LABELS = { xenos: 'Newcomer', thetes: 'Contributor', metic: 'Trusted', archon: 'Owner' };
|
|
54
|
+
const rankLabel = (r) => RANK_LABELS[r] || String(r || '');
|
|
55
|
+
|
|
56
|
+
// The viewer is builder 1. Rows as GET /builders/roster returns them — note the
|
|
57
|
+
// viewer sits in the MIDDLE, which is where the live roster puts the owner: the
|
|
58
|
+
// order is inactive-last, then still-onboarding, then created_at DESC.
|
|
59
|
+
const VIEWER_ID = '1';
|
|
60
|
+
const ROSTER = [
|
|
61
|
+
{ id: '90', display_name: 'Alpha', github_login: 'alpha', rank: 'archon', total_credits: 9200, karma: 4, active_claim_count: 1, stale_claim: false, status: 'active' },
|
|
62
|
+
{ id: '1', display_name: 'The Owner', github_login: 'theowner', rank: 'archon', total_credits: 6896, karma: 68, active_claim_count: 2, stale_claim: false, status: 'active' },
|
|
63
|
+
{ id: '92', display_name: 'Gamma', github_login: 'gamma', rank: 'thetes', total_credits: 830, karma: 1, active_claim_count: 0, stale_claim: false, status: 'active' },
|
|
64
|
+
{ id: '93', display_name: 'Delta', github_login: 'delta', rank: 'xenos', total_credits: 40, karma: 0, active_claim_count: 0, stale_claim: false, status: 'inactive' },
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
// ===========================================================================
|
|
68
|
+
// Boot the real page against a scripted API and hand back handles.
|
|
69
|
+
// ===========================================================================
|
|
70
|
+
|
|
71
|
+
async function boot(opts = {}) {
|
|
72
|
+
const {
|
|
73
|
+
builders = ROSTER,
|
|
74
|
+
me = { id: VIEWER_ID, rank: 'archon', github_login: 'theowner' },
|
|
75
|
+
patchResult = null,
|
|
76
|
+
} = opts;
|
|
77
|
+
|
|
78
|
+
const requests = [];
|
|
79
|
+
const toasts = [];
|
|
80
|
+
const prompts = [];
|
|
81
|
+
|
|
82
|
+
const api = {
|
|
83
|
+
async request(method, p, init) {
|
|
84
|
+
const body = init && init.body;
|
|
85
|
+
requests.push({ method, path: p, body });
|
|
86
|
+
const route = String(p).replace('/api/bongos', '').split('?')[0];
|
|
87
|
+
if (method === 'GET' && route === '/me') return ok({ builder: me });
|
|
88
|
+
if (method === 'GET' && route === '/builders/roster') return ok({ builders });
|
|
89
|
+
if (method === 'PATCH' && route.startsWith('/builders/')) {
|
|
90
|
+
return patchResult || ok({ builder: { id: route.split('/')[2] } });
|
|
91
|
+
}
|
|
92
|
+
// Every other panel on this page loads independently and degrades on its
|
|
93
|
+
// own; an unscripted shape is exactly the failure `section` swallows.
|
|
94
|
+
return ok({});
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const documentObj = {
|
|
99
|
+
readyState: 'complete',
|
|
100
|
+
body: makeEl('body'),
|
|
101
|
+
documentElement: makeEl('html'),
|
|
102
|
+
createElement: (tag) => makeEl(tag),
|
|
103
|
+
getElementById(id) { return this.body.querySelector(`#${id}`); },
|
|
104
|
+
querySelector(sel) { return this.body.querySelector(sel); },
|
|
105
|
+
querySelectorAll(sel) { return this.body.querySelectorAll(sel); },
|
|
106
|
+
addEventListener() {},
|
|
107
|
+
};
|
|
108
|
+
parseInto(documentObj.body, PAGE);
|
|
109
|
+
|
|
110
|
+
const locationObj = { hostname: 'builders.example.test', pathname: '/builders/watch', search: '', hash: '', assign() {} };
|
|
111
|
+
|
|
112
|
+
const kit = {
|
|
113
|
+
makeTabs: () => ({}),
|
|
114
|
+
// A ledger that REALLY renders: the whole question here is which row got
|
|
115
|
+
// which cell, and a no-op render answers none of it.
|
|
116
|
+
makeLedger: ({ mount, columns, rowHtml }) => ({
|
|
117
|
+
render(rows) {
|
|
118
|
+
mount.innerHTML =
|
|
119
|
+
'<table class="ledger"><thead><tr>' +
|
|
120
|
+
columns.map((c) => `<th>${escapeHtml(typeof c === 'string' ? c : c.label)}</th>`).join('') +
|
|
121
|
+
'</tr></thead><tbody>' + rows.map((r) => rowHtml(r)).join('') + '</tbody></table>';
|
|
122
|
+
},
|
|
123
|
+
}),
|
|
124
|
+
makePager: () => ({ page: 1, perPage: 25 }),
|
|
125
|
+
emptyStateHtml: (msg) => `<div class="empty-state"><p>${escapeHtml(msg)}</p></div>`,
|
|
126
|
+
makeFilterBar: (o) => { o.mount.innerHTML = '<div class="filters"></div>'; return { el: o.mount, values: () => ({}) }; },
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const windowObj = {
|
|
130
|
+
__BRANDING__: { project: { joinability: 'apply' } },
|
|
131
|
+
OTB: {
|
|
132
|
+
escapeHtml,
|
|
133
|
+
$: (sel) => documentObj.querySelector(sel),
|
|
134
|
+
num: (v) => Number(v),
|
|
135
|
+
fmtInt: (n) => String(n),
|
|
136
|
+
fmtCompact: (n) => String(n),
|
|
137
|
+
fmtDate: (iso) => String(iso).slice(0, 10),
|
|
138
|
+
rankLabel,
|
|
139
|
+
toast: (m) => toasts.push(m),
|
|
140
|
+
},
|
|
141
|
+
OTBKit: kit,
|
|
142
|
+
OTBBranding: { currencyLabel: () => 'Credits' },
|
|
143
|
+
BongosClient: { createClient: () => api },
|
|
144
|
+
location: locationObj,
|
|
145
|
+
prompt: (msg) => { prompts.push(msg); return 'left for a new job'; },
|
|
146
|
+
addEventListener() {},
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const sandbox = {
|
|
150
|
+
window: windowObj, document: documentObj, location: locationObj,
|
|
151
|
+
console: { log() {}, warn() {}, error() {} },
|
|
152
|
+
setTimeout, clearTimeout, Promise, Date, Math, JSON, Error, isNaN,
|
|
153
|
+
Number, String, Object, Array, Boolean, Set, Map, RegExp,
|
|
154
|
+
encodeURIComponent, decodeURIComponent, parseInt, parseFloat,
|
|
155
|
+
};
|
|
156
|
+
sandbox.globalThis = sandbox;
|
|
157
|
+
vm.createContext(sandbox);
|
|
158
|
+
vm.runInContext(SRC, sandbox, { filename: 'watch.js' });
|
|
159
|
+
await settle();
|
|
160
|
+
|
|
161
|
+
const rosterBody = () => documentObj.getElementById('roster-body');
|
|
162
|
+
const rows = () => rosterBody().querySelectorAll('tr').filter((tr) => tr.querySelectorAll('td').length);
|
|
163
|
+
const rowFor = (login) => rows().find((tr) => tr.querySelectorAll('td')[0].textContent.includes('@' + login));
|
|
164
|
+
const patches = () => requests.filter((r) => r.method === 'PATCH');
|
|
165
|
+
const footText = () => rosterBody().querySelectorAll('p').map((p) => p.textContent).join(' ');
|
|
166
|
+
|
|
167
|
+
return { requests, toasts, prompts, documentObj, rosterBody, rows, rowFor, patches, footText, settle };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const cells = (tr) => tr.querySelectorAll('td');
|
|
171
|
+
|
|
172
|
+
// ===========================================================================
|
|
173
|
+
// 1. The viewer's own row offers neither control.
|
|
174
|
+
// ===========================================================================
|
|
175
|
+
|
|
176
|
+
await test('the viewer\'s own row renders no Offboard button', async () => {
|
|
177
|
+
const h = await boot();
|
|
178
|
+
const mine = h.rowFor('theowner');
|
|
179
|
+
assert.ok(mine, 'the roster did not render the viewer\'s own row');
|
|
180
|
+
assert.equal(mine.querySelectorAll('[data-roster-status]').length, 0,
|
|
181
|
+
'the viewer\'s own row still carries a status button the server refuses outright');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
await test('the viewer\'s own row renders no rank select', async () => {
|
|
185
|
+
const h = await boot();
|
|
186
|
+
const mine = h.rowFor('theowner');
|
|
187
|
+
assert.equal(mine.querySelectorAll('[data-roster-rank]').length, 0,
|
|
188
|
+
'the viewer\'s own row still carries a rank select the server refuses outright');
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
await test('the own row still SHOWS the rank and says whose row it is', async () => {
|
|
192
|
+
const h = await boot();
|
|
193
|
+
const c = cells(h.rowFor('theowner'));
|
|
194
|
+
assert.equal(c[1].textContent.trim(), 'Owner', 'the own row lost the rank it should still state');
|
|
195
|
+
assert.match(c[6].textContent, /you/i, 'the own row\'s action cell says nothing about why it is empty');
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// ===========================================================================
|
|
199
|
+
// 2. Every OTHER row keeps both controls — the fix must not disarm the roster.
|
|
200
|
+
// ===========================================================================
|
|
201
|
+
|
|
202
|
+
await test('every other row keeps its Offboard / Reactivate button', async () => {
|
|
203
|
+
const h = await boot();
|
|
204
|
+
for (const login of ['alpha', 'gamma', 'delta']) {
|
|
205
|
+
const tr = h.rowFor(login);
|
|
206
|
+
assert.equal(tr.querySelectorAll('[data-roster-status]').length, 1,
|
|
207
|
+
`row @${login} lost its status control`);
|
|
208
|
+
}
|
|
209
|
+
// The inactive builder offers the OTHER verb, as before.
|
|
210
|
+
assert.equal(h.rowFor('delta').querySelector('[data-roster-status]').getAttribute('data-roster-status'), 'active');
|
|
211
|
+
assert.equal(h.rowFor('alpha').querySelector('[data-roster-status]').getAttribute('data-roster-status'), 'inactive');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
await test('every other row keeps its rank select', async () => {
|
|
215
|
+
const h = await boot();
|
|
216
|
+
for (const login of ['alpha', 'gamma', 'delta']) {
|
|
217
|
+
assert.equal(h.rowFor(login).querySelectorAll('[data-roster-rank]').length, 1,
|
|
218
|
+
`row @${login} lost its rank select`);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
await test('another builder\'s Offboard still reaches the route', async () => {
|
|
223
|
+
const h = await boot();
|
|
224
|
+
const btn = h.rowFor('gamma').querySelector('[data-roster-status]');
|
|
225
|
+
// The roster DELEGATES: the listener is on #roster-body, not on the button, so
|
|
226
|
+
// a list that pages cannot strand a row without one. The DOM stub's fire()
|
|
227
|
+
// does not bubble, so firing on the button reaches nothing — dispatch on the
|
|
228
|
+
// section and name the button as the target, the way a real click arrives.
|
|
229
|
+
h.rosterBody().fire('click', { target: btn });
|
|
230
|
+
await h.settle();
|
|
231
|
+
const p = h.patches();
|
|
232
|
+
assert.equal(p.length, 1, `expected one PATCH, got ${p.length}`);
|
|
233
|
+
assert.match(p[0].path, /\/builders\/92\/status$/);
|
|
234
|
+
assert.equal(p[0].body.status, 'inactive');
|
|
235
|
+
assert.equal(p[0].body.reason, 'left for a new job', 'the offboarding reason was dropped');
|
|
236
|
+
assert.equal(h.prompts.length, 1, 'the reason prompt no longer runs');
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// ===========================================================================
|
|
240
|
+
// 3. The foot line says why, in the pack's own words.
|
|
241
|
+
// ===========================================================================
|
|
242
|
+
|
|
243
|
+
await test('a foot line under the roster explains the empty row', async () => {
|
|
244
|
+
const h = await boot();
|
|
245
|
+
assert.match(h.footText(), /your own row carries no controls/i,
|
|
246
|
+
'nothing under the roster explains why one row has no controls');
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
await test('the foot line names the rank from the pack, never a hardcoded "Archon"', async () => {
|
|
250
|
+
const h = await boot();
|
|
251
|
+
const foot = h.footText();
|
|
252
|
+
assert.match(foot, /Owner/, 'the foot line does not name the rank through rankLabel()');
|
|
253
|
+
assert.ok(!/archon/i.test(foot), `the foot line hardcodes a rank word: ${foot}`);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
await test('no foot line when the viewer is not on the roster', async () => {
|
|
257
|
+
// A Metic reading the roster of a project they are not listed on: there is no
|
|
258
|
+
// own row, so there is nothing to explain.
|
|
259
|
+
const h = await boot({ builders: ROSTER.filter((b) => b.id !== VIEWER_ID) });
|
|
260
|
+
assert.ok(!/your own row/i.test(h.footText()), 'the foot line rendered with no own row on the table');
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// ===========================================================================
|
|
264
|
+
// 4. Degrade: an unidentifiable viewer must not mark the WRONG row.
|
|
265
|
+
// ===========================================================================
|
|
266
|
+
|
|
267
|
+
await test('no id from /me leaves every row exactly as it was', async () => {
|
|
268
|
+
const h = await boot({ me: { rank: 'archon', github_login: 'theowner' } });
|
|
269
|
+
assert.equal(h.rows().length, ROSTER.length);
|
|
270
|
+
for (const b of ROSTER) {
|
|
271
|
+
assert.equal(h.rowFor(b.github_login).querySelectorAll('[data-roster-status]').length, 1,
|
|
272
|
+
`row @${b.github_login} was disarmed although the viewer could not be identified`);
|
|
273
|
+
}
|
|
274
|
+
assert.ok(!/your own row/i.test(h.footText()));
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
await test('the match is by value, not by type — a numeric id still matches', async () => {
|
|
278
|
+
// node-postgres hands bigints back as TEXT, so /me may answer with a string
|
|
279
|
+
// where the roster answers with a number (or the reverse). A `===` comparison
|
|
280
|
+
// here is the same bug the /rank route's own guard was fixed for in #348.
|
|
281
|
+
const h = await boot({
|
|
282
|
+
me: { id: 1, rank: 'archon', github_login: 'theowner' },
|
|
283
|
+
builders: ROSTER.map((b) => (b.id === VIEWER_ID ? { ...b, id: '1' } : b)),
|
|
284
|
+
});
|
|
285
|
+
assert.equal(h.rowFor('theowner').querySelectorAll('[data-roster-status]').length, 0,
|
|
286
|
+
'a numeric /me id did not match the roster\'s string id — the own row is armed again');
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
summary('watch roster — the viewer\'s own row');
|