@bongos/core 1.19.420 → 1.19.421
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 +68 -33
- package/docs/copy-inventory.md +282 -281
- package/docs/copy-registry.json +375 -366
- package/docs/design/mocks/hub/mine.html +377 -0
- package/docs/design/projects-hub-direction-v2.md +25 -1
- package/docs/file-map.md +1 -0
- package/docs/module-api-changelog.md +2 -0
- package/modules/hall-ui/public/settings.html +3 -2
- package/modules/hall-ui/public/settings.js +10 -6
- package/modules/platform-identity/account-visibility.js +94 -3
- package/modules/platform-identity/platform-identity.js +45 -50
- package/modules/platform-identity/routes/connections.js +3 -3
- package/modules/ui-design/kit/fixtures/featured-hub.json +1 -0
- package/modules/ui-design/kit/fixtures/provisioning-instances-empty.json +4 -0
- package/modules/ui-design/kit/fixtures/provisioning-instances.json +99 -0
- package/modules/ui-design/kit/lib.js +6 -2
- package/modules/ui-design/kit/probe.js +1 -1
- package/modules/ui-design/kit/render.js +1 -1
- package/modules/ui-design/kit/serve.js +63 -2
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/public-landing/assets/cosmos.css +224 -18
- package/public-landing/projects-empty.states.json +49 -0
- package/public-landing/projects-floor.states.json +63 -0
- package/public-landing/projects.html +112 -51
- package/public-landing/projects.probes.json +32 -7
- package/public-landing/projects.states.json +10 -6
- package/src/module-api.js +1 -1
- package/tests/account_privacy_flags.mjs +17 -10
- package/tests/effective_visibility_predicate.mjs +891 -0
- package/tests/profile_route.mjs +22 -11
- package/tests/projects_hub.mjs +33 -5
- package/tests/projects_hub_pre_uat.mjs +18 -6
- package/tests/ui_design_kit.mjs +32 -2
|
@@ -50,15 +50,10 @@
|
|
|
50
50
|
#view-mine .hero,#view-manage .hero{max-width:none;}
|
|
51
51
|
|
|
52
52
|
/* ── the manage view (task 1003041): container rhythm only — every component
|
|
53
|
-
it draws (
|
|
54
|
-
|
|
53
|
+
it draws (the head, the ledger, the choice cells, the rail, the doors) is
|
|
54
|
+
cosmos vocabulary, scoped to #view-manage there (task 1003345) ── */
|
|
55
55
|
#view-manage .empty{text-align:left;}
|
|
56
56
|
#view-manage .empty p{margin-bottom:0;}
|
|
57
|
-
#view-manage .visOpt{display:flex;gap:10px;align-items:flex-start;padding:10px 12px;border:1px solid var(--rule);border-radius:10px;margin-bottom:8px;cursor:pointer;}
|
|
58
|
-
#view-manage .visOpt:has(input:checked){border-color:var(--accent);}
|
|
59
|
-
#view-manage .visOpt input{margin-top:4px;flex:none;}
|
|
60
|
-
#view-manage .visOpt b{display:block;font-size:15px;}
|
|
61
|
-
#view-manage .visOpt span{display:block;font-size:13px;color:var(--ink-soft);line-height:1.45;}
|
|
62
57
|
|
|
63
58
|
/* The bongo pair on the New-project view. Component-scoped custom properties,
|
|
64
59
|
never :root (C1). The drum head is SKIN — using --bg-card would turn it black
|
|
@@ -576,8 +571,8 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
576
571
|
<div class="wrap viewWrap">
|
|
577
572
|
<div class="hero" style="padding-left:0;padding-right:0">
|
|
578
573
|
<h1>Your projects</h1>
|
|
579
|
-
<p class="lede">Every project you own
|
|
580
|
-
Each
|
|
574
|
+
<p class="lede">Every project you own: what it is, whether it is answering, and the controls that matter.
|
|
575
|
+
Each row says what the platform <b>last verified</b>, and when.</p>
|
|
581
576
|
</div>
|
|
582
577
|
|
|
583
578
|
<div class="card">
|
|
@@ -617,11 +612,12 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
617
612
|
<p class="mineEmpty" id="mineEmpty" hidden>Nothing matches that. <button type="button" class="linklike" id="mineClear">Clear the filters</button></p>
|
|
618
613
|
|
|
619
614
|
<section class="empty" id="empty" hidden>
|
|
620
|
-
<
|
|
615
|
+
<h2>nothing of yours in this sky yet</h2>
|
|
616
|
+
<p>Name a project, pick a home for its code, and it gets an address of its own here. It shows up in this list the moment you file it.</p>
|
|
621
617
|
<a class="btn" href="?view=new" data-nav-to="new">Create your first project</a>
|
|
622
618
|
</section>
|
|
623
619
|
<ul class="projects" id="projectsList"></ul>
|
|
624
|
-
<p class="finePrint">Buttons here ask the platform to act; each
|
|
620
|
+
<p class="finePrint">Buttons here ask the platform to act; each row reports only what was verified, and when. A standup keeps going even with this tab closed.</p>
|
|
625
621
|
</section>
|
|
626
622
|
</div>
|
|
627
623
|
</div>
|
|
@@ -640,6 +636,19 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
640
636
|
<div class="wrap viewWrap">
|
|
641
637
|
<p class="backRow"><a href="?view=mine" data-nav-to="mine">← Your projects</a></p>
|
|
642
638
|
|
|
639
|
+
<!-- the head stands in EVERY state (task 1003345): the page's one h1 lives
|
|
640
|
+
here, outside #mMain, so the signed-out, not-found and loading states
|
|
641
|
+
keep a visible heading (the kit's one-header-main-h1 floor). The planet
|
|
642
|
+
and the chip fill only once a project has rendered; the clause hides
|
|
643
|
+
with #mMain (cosmos, :has). -->
|
|
644
|
+
<div class="hero mHead" style="padding-left:0;padding-right:0">
|
|
645
|
+
<span id="mOrb"></span>
|
|
646
|
+
<div>
|
|
647
|
+
<div class="titleRow"><h1 id="mTitle"><span id="mName">Manage</span></h1><span id="mChip"></span></div>
|
|
648
|
+
<p class="lede">Its address, its repository, what the platform last <b>verified</b> about it, and the controls you have today.</p>
|
|
649
|
+
</div>
|
|
650
|
+
</div>
|
|
651
|
+
|
|
643
652
|
<!-- signed out: the same identity-only door the mine view uses (ADR 0143) -->
|
|
644
653
|
<section id="mGate" class="card" hidden>
|
|
645
654
|
<p class="lede">This is a project’s management page — it answers only to the person who owns it. Sign in with GitHub to open yours.</p>
|
|
@@ -660,19 +669,18 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
660
669
|
<div class="loading" id="mLoading">Reading the project…</div>
|
|
661
670
|
|
|
662
671
|
<section id="mMain" hidden>
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
<p class="finePrint" id="mCreated" hidden></p>
|
|
672
|
+
<!-- the ledger of facts (task 1003345): each fact keeps its id and fills
|
|
673
|
+
as one label · value pair; a fact with no answer hides — never a dash -->
|
|
674
|
+
<div class="card" id="mFacts">
|
|
675
|
+
<div class="ledger">
|
|
676
|
+
<div id="mMeta" hidden></div>
|
|
677
|
+
<div id="mAddr" hidden></div>
|
|
678
|
+
<div id="mCreated" hidden></div>
|
|
679
|
+
<div id="mPlanet" hidden></div>
|
|
680
|
+
</div>
|
|
673
681
|
<!-- the card's own story, on the project's own page (task 1003232): the
|
|
674
|
-
per-flow
|
|
675
|
-
stripFor/errMarkup/canRetry/canRevive the list
|
|
682
|
+
per-flow rail, the honest snag, and the doors — the same
|
|
683
|
+
stripFor/errMarkup/canRetry/canRevive the list row reads -->
|
|
676
684
|
<div id="mStrip"></div>
|
|
677
685
|
<div class="projMsg err" id="mErr" hidden></div>
|
|
678
686
|
<div class="projMsg" id="mMsg" role="status" aria-live="polite"></div>
|
|
@@ -684,8 +692,8 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
684
692
|
private direction is refused (server + copy) until the private-repo
|
|
685
693
|
deploy fix lands, because the deploy pull cannot read a private repo
|
|
686
694
|
(walk finding N4). -->
|
|
687
|
-
<div class="card" id="mRepoVisibility">
|
|
688
|
-
<h2
|
|
695
|
+
<div class="card mSec" id="mRepoVisibility">
|
|
696
|
+
<h2>Repository visibility</h2>
|
|
689
697
|
<div id="mRepoVis"><p class="finePrint">Checking the repository on GitHub…</p></div>
|
|
690
698
|
<!-- the limitation itself, one click away (grade follow-up task 1003077):
|
|
691
699
|
the refusal above states it; this fold explains it and links the
|
|
@@ -704,8 +712,8 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
704
712
|
a change reaches a live project only by the restart the runner
|
|
705
713
|
performs (ADR 0190). The fold is a SIBLING of the volatile slot (the
|
|
706
714
|
#mRepoVisWhy shape) so no re-render can drop it. -->
|
|
707
|
-
<div class="card" id="mVisibility">
|
|
708
|
-
<h2
|
|
715
|
+
<div class="card mSec" id="mVisibility">
|
|
716
|
+
<h2>Who can see it</h2>
|
|
709
717
|
<div id="mVis"><p class="finePrint">Reading the project’s settings…</p></div>
|
|
710
718
|
<details class="finePrint" id="mVisWhat">
|
|
711
719
|
<summary>What “only signed-in members” changes</summary>
|
|
@@ -719,8 +727,8 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
719
727
|
RUNS is read back from its own public manifest (brand.project.
|
|
720
728
|
joinability) — never from the save's reply, which is a receipt. The
|
|
721
729
|
fold is a SIBLING of the volatile slot so no re-render can drop it. -->
|
|
722
|
-
<div class="card" id="mJoinability">
|
|
723
|
-
<h2
|
|
730
|
+
<div class="card mSec" id="mJoinability">
|
|
731
|
+
<h2>How people join</h2>
|
|
724
732
|
<div id="mJoin"><p class="finePrint">Reading the project’s settings…</p></div>
|
|
725
733
|
<details class="finePrint" id="mJoinWhat">
|
|
726
734
|
<summary>What each choice changes</summary>
|
|
@@ -736,8 +744,8 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
736
744
|
much as for a healthy one, which is why it asks for the project's own
|
|
737
745
|
name typed back: it is the only control on this page that ends
|
|
738
746
|
something, and the card's one-click confirm is too light for it. -->
|
|
739
|
-
<div class="card" id="mDelete">
|
|
740
|
-
<h2
|
|
747
|
+
<div class="card mSec" id="mDelete">
|
|
748
|
+
<h2>Take it offline</h2>
|
|
741
749
|
<div id="mDel"><p class="finePrint">Reading the project…</p></div>
|
|
742
750
|
<!-- the teardown-vs-delete distinction, stated once as a SIBLING of the
|
|
743
751
|
volatile slot (the #mRepoVisWhy shape) so no re-render can drop it.
|
|
@@ -1284,7 +1292,6 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1284
1292
|
were orphans). Keeping the per-project variety is what makes a sky legible;
|
|
1285
1293
|
deriving it from contract tokens is what makes it survive a re-brand. */
|
|
1286
1294
|
var ORB_ACCENTS = ['var(--sea)', 'var(--terracotta)', 'var(--gold-soft)', 'var(--sun-lit)', 'var(--accent)'];
|
|
1287
|
-
var ORB_GLOWS = ORB_ACCENTS.map(function (t) { return 'color-mix(in srgb,' + t + ' 45%,transparent)'; });
|
|
1288
1295
|
/* THE PLANET TEMPLATES (task 1003319; the materials, task 1003337). A project's
|
|
1289
1296
|
orb is one of the world's three whole objects (blob / pair / bead) in one of
|
|
1290
1297
|
four materials (chrome — the first round, named by omission — frost, gold,
|
|
@@ -2009,7 +2016,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
2009
2016
|
function loadCatalog() {
|
|
2010
2017
|
fetch(API + '/projects/featured', { credentials: 'same-origin' })
|
|
2011
2018
|
.then(function (res) { if (!res.ok) throw new Error('catalog'); return res.json(); })
|
|
2012
|
-
.then(function (data) { catalog = (data && data.projects) || []; assignAccents(); applyMapFilter(); })
|
|
2019
|
+
.then(function (data) { catalog = (data && data.projects) || []; assignAccents(); applyMapFilter(); repaintPlanets(); })
|
|
2013
2020
|
.catch(function () {
|
|
2014
2021
|
/* a child instance has no catalog module at all — say so plainly
|
|
2015
2022
|
rather than leave an empty sky with no explanation */
|
|
@@ -4249,19 +4256,57 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
4249
4256
|
return !!inst.open_intent; /* e.g. a queued teardown or domain-attach on an active instance */
|
|
4250
4257
|
}
|
|
4251
4258
|
|
|
4252
|
-
/* ── the
|
|
4253
|
-
|
|
4259
|
+
/* ── the planet on a row (task 1003345): the SAME object the sky shows. A
|
|
4260
|
+
catalog-matched instance takes its catalog row's template and accent
|
|
4261
|
+
(curated, or position-walked by assignAccents); an owned-only one walks
|
|
4262
|
+
by its slug, the rule the map's appended entries use — so one project is
|
|
4263
|
+
one object in both views and never re-colours between them. ── */
|
|
4264
|
+
function planetOf(inst) {
|
|
4265
|
+
var h = inst.domain ? hostOf(inst.domain) : '';
|
|
4266
|
+
for (var i = 0; h && i < catalog.length; i++) {
|
|
4267
|
+
if (hostOf(catalog[i].origin) === h) {
|
|
4268
|
+
return { tpl: templateOf(catalog[i]), acc: accentOf(catalog[i]), curated: ORB_TEMPLATES.indexOf(catalog[i].template) !== -1 };
|
|
4269
|
+
}
|
|
4270
|
+
}
|
|
4271
|
+
return { tpl: slugTemplate(inst.slug), acc: ORB_ACCENTS[slugAccent(inst.slug)], curated: false };
|
|
4272
|
+
}
|
|
4273
|
+
/* shape[-material]-style → the data attributes cosmos.css draws from (the map's rule) */
|
|
4274
|
+
function planetAttrs(pl) {
|
|
4275
|
+
var parts = pl.tpl.split('-');
|
|
4276
|
+
return ' data-template="' + esc(pl.tpl) + '" data-shape="' + esc(parts[0]) +
|
|
4277
|
+
'" data-material="' + esc(parts.length === 3 ? parts[1] : 'chrome') +
|
|
4278
|
+
'" data-style="' + esc(parts[parts.length - 1]) + '"';
|
|
4279
|
+
}
|
|
4280
|
+
var PLANET_SHAPE_WORDS = { blob: 'the blob', pair: 'the bongo pair', bead: 'the bead' };
|
|
4281
|
+
var PLANET_STYLE_WORDS = { tint: 'tinted', wash: 'washed', plain: 'plain', chrome: 'plain' };
|
|
4282
|
+
/* the planet as a clause for the manage ledger: the accent as a dot, then shape, material, style */
|
|
4283
|
+
function planetWords(pl) {
|
|
4284
|
+
var parts = pl.tpl.split('-');
|
|
4285
|
+
var material = parts.length === 3 ? parts[1] : 'chrome';
|
|
4286
|
+
return '<span class="swDot" style="--sw:' + pl.acc + '" aria-hidden="true"></span>' +
|
|
4287
|
+
esc((PLANET_SHAPE_WORDS[parts[0]] || parts[0]) + ' in ' + material + ', ' +
|
|
4288
|
+
(PLANET_STYLE_WORDS[parts[parts.length - 1]] || parts[parts.length - 1]));
|
|
4289
|
+
}
|
|
4290
|
+
/* the catalog can land after a view rendered — the rows and the manage head take its planets then */
|
|
4291
|
+
function repaintPlanets() {
|
|
4292
|
+
Object.keys(byId).forEach(function (id) { renderCard(byId[id]); });
|
|
4293
|
+
if (manageInst) paintManagePlanet(manageInst);
|
|
4294
|
+
}
|
|
4295
|
+
|
|
4296
|
+
/* ── the row's object: the planet, its state on the object (ghost = torn
|
|
4297
|
+
down, ashen = failed its check, dim = a stale probe); only a live
|
|
4298
|
+
project's planet is a door to its site ── */
|
|
4254
4299
|
function orbFor(inst) {
|
|
4255
|
-
var
|
|
4256
|
-
var cls = '
|
|
4300
|
+
var pl = planetOf(inst);
|
|
4301
|
+
var cls = 'worldOrb';
|
|
4257
4302
|
if (inst.status === 'torn_down' || inst.status === 'tearing_down') cls += ' ghost';
|
|
4258
4303
|
else if (inst.status === 'error' || (inst.status === 'active' && inst.last_verified_up === false)) cls += ' ashen';
|
|
4259
4304
|
else if (inst.status === 'active' && !freshUp(inst)) cls += ' dim';
|
|
4260
|
-
var
|
|
4305
|
+
var attrs = planetAttrs(pl) + ' style="--acc:' + pl.acc + ';--d:64px"';
|
|
4261
4306
|
if (inst.status === 'active' && inst.domain) {
|
|
4262
|
-
return '<a class="' + cls + '"
|
|
4307
|
+
return '<a class="' + cls + '"' + attrs + ' href="https://' + esc(inst.domain) + '" target="_blank" rel="noopener" title="Open ' + esc(inst.slug) + '" aria-label="Open ' + esc(inst.slug) + '"><span class="disc"></span></a>';
|
|
4263
4308
|
}
|
|
4264
|
-
return '<span class="' + cls + '"
|
|
4309
|
+
return '<span class="' + cls + '"' + attrs + ' aria-hidden="true"><span class="disc"></span></span>';
|
|
4265
4310
|
}
|
|
4266
4311
|
|
|
4267
4312
|
/* ── progress strips: each flow speaks its own stages (direction §4).
|
|
@@ -4747,6 +4792,17 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
4747
4792
|
encodeURIComponent('/projects?view=manage&id=' + manageId);
|
|
4748
4793
|
}
|
|
4749
4794
|
|
|
4795
|
+
/* one label · value pair of the manage ledger */
|
|
4796
|
+
function ledgerRow(r) { return '<span class="k">' + r[0] + '</span><span class="v">' + r[1] + '</span>'; }
|
|
4797
|
+
/* the head's planet and the ledger's planet row (task 1003345) — one resolution
|
|
4798
|
+
(planetOf) for both, so the head, the row and the sky can never disagree; the
|
|
4799
|
+
planet row is the slot the picker (task 1003347) fills */
|
|
4800
|
+
function paintManagePlanet(inst) {
|
|
4801
|
+
var pl = planetOf(inst);
|
|
4802
|
+
$('mOrb').innerHTML = orbFor(inst);
|
|
4803
|
+
$('mPlanet').innerHTML = ledgerRow(['planet', planetWords(pl) + (pl.curated ? '' : '<small>chosen for it by the platform.</small>')]);
|
|
4804
|
+
$('mPlanet').hidden = false;
|
|
4805
|
+
}
|
|
4750
4806
|
function renderManage(inst) {
|
|
4751
4807
|
manageInst = inst;
|
|
4752
4808
|
$('mLoading').hidden = true;
|
|
@@ -4757,20 +4813,22 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
4757
4813
|
/* the ONE chip site of this view — chipFor() or nothing, so the manage
|
|
4758
4814
|
page can never tell a different liveness story than the card (C6) */
|
|
4759
4815
|
$('mChip').innerHTML = chipFor(inst);
|
|
4760
|
-
|
|
4816
|
+
paintManagePlanet(inst);
|
|
4817
|
+
var rows = [];
|
|
4761
4818
|
if (inst.target_ref) {
|
|
4762
|
-
|
|
4763
|
-
'" target="_blank" rel="noopener"
|
|
4819
|
+
rows.push(['repository', '<a href="https://github.com/' + esc(inst.target_ref) +
|
|
4820
|
+
'" target="_blank" rel="noopener">' + esc(inst.target_ref) + '</a>']);
|
|
4764
4821
|
}
|
|
4765
|
-
|
|
4766
|
-
if (inst.onboard_mode === 'adopt')
|
|
4767
|
-
|
|
4768
|
-
$('mMeta').
|
|
4822
|
+
var server = inst.hosting_shape ? esc(SHAPE_LABEL[inst.hosting_shape] || inst.hosting_shape) : '';
|
|
4823
|
+
if (inst.onboard_mode === 'adopt') server += (server ? ', ' : '') + 'adopted repo';
|
|
4824
|
+
if (server) rows.push(['server', server]);
|
|
4825
|
+
$('mMeta').innerHTML = rows.map(ledgerRow).join('');
|
|
4826
|
+
$('mMeta').hidden = !rows.length;
|
|
4769
4827
|
var addr = addressRow(inst);
|
|
4770
|
-
$('mAddr').innerHTML = addr;
|
|
4828
|
+
$('mAddr').innerHTML = addr ? ledgerRow(['address', addr]) : '';
|
|
4771
4829
|
$('mAddr').hidden = !addr;
|
|
4772
4830
|
/* a fact with no answer hides — never a dash (the card rule) */
|
|
4773
|
-
$('mCreated').
|
|
4831
|
+
$('mCreated').innerHTML = inst.created_at ? ledgerRow(['created', esc(listedDate(inst.created_at))]) : '';
|
|
4774
4832
|
$('mCreated').hidden = !inst.created_at;
|
|
4775
4833
|
/* fetch-free like everything else here, so the once-a-minute tick may call
|
|
4776
4834
|
it: the delete section reads only status + open_intent, both already in
|
|
@@ -4780,8 +4838,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
4780
4838
|
only, so a queued settings push shows up on the tick without a reload */
|
|
4781
4839
|
renderVisState(inst);
|
|
4782
4840
|
renderJoinState(inst);
|
|
4783
|
-
/* the page names its project —
|
|
4784
|
-
$('mTitle').textContent = inst.slug || 'Manage';
|
|
4841
|
+
/* the page names its project — the heading holds #mName; and in the tab */
|
|
4785
4842
|
document.title = (inst.slug ? inst.slug + ' — ' : '') + BASE_TITLE;
|
|
4786
4843
|
/* the card's own story, on the project's own page (task 1003232): the
|
|
4787
4844
|
per-flow strip, the honest snag, and the doors the list card has */
|
|
@@ -4880,6 +4937,10 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
4880
4937
|
$('mGate').hidden = true;
|
|
4881
4938
|
$('mMissing').hidden = true;
|
|
4882
4939
|
$('mLoading').hidden = false;
|
|
4940
|
+
/* the head stands in every state — a fresh load must not wear the last project's name */
|
|
4941
|
+
$('mName').textContent = 'Manage';
|
|
4942
|
+
$('mChip').innerHTML = '';
|
|
4943
|
+
$('mOrb').innerHTML = '';
|
|
4883
4944
|
}
|
|
4884
4945
|
var asked = manageId; /* a stale answer must never paint a newer project */
|
|
4885
4946
|
fetch(API + '/provisioning/instances/' + encodeURIComponent(manageId), { credentials: 'same-origin' })
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_": "The hub's interaction contract for modules/ui-design/kit/probe.js (task 1003320): the disclosure bands, the views, the card, the fold under 480, signed out. A probe with `continue` keeps the previous page — the contract is a sequence.",
|
|
2
|
+
"_": "The hub's interaction contract for modules/ui-design/kit/probe.js (task 1003320): the disclosure bands, the views, the card, the fold under 480, signed out. A probe with `continue` keeps the previous page — the contract is a sequence. The manage flows (task 1003345): attach, restart, teardown (the row and the ender), revive, retry and join drive the stub's receipts end to end, so the makeover cannot silently break a door. The catalog is featured-hub.json (stub.fixtures): the signed-in stub owns Demo, and an owned orb's plain click opens its manage view, never its card — the card probes open the landing's other rows and Tidewater, the un-owned floor row.",
|
|
3
3
|
"page": "/projects",
|
|
4
4
|
"surface": "public-landing",
|
|
5
|
+
"stub": { "fixtures": { "featured": "featured-hub" } },
|
|
5
6
|
"probes": [
|
|
6
7
|
{ "name": "closed: the main content starts under the bar", "auth": true, "width": 1440,
|
|
7
8
|
"steps": [],
|
|
@@ -46,7 +47,7 @@
|
|
|
46
47
|
"steps": [["click", "#menuProfile a[data-nav-to=\"mine\"]"], ["wait", 300]],
|
|
47
48
|
"expect": { "param": { "view": "mine" }, "closed": "#menuProfile", "text": [["[aria-current=\"page\"]", "Your projects"]] } },
|
|
48
49
|
{ "name": "an orb opens the card", "continue": true,
|
|
49
|
-
"steps": [["back"], ["wait", 400], ["dispatchClick", "#orbField a.worldOrb:not(#selfOrb)"], ["wait", 400]],
|
|
50
|
+
"steps": [["back"], ["wait", 400], ["dispatchClick", "#orbField a.worldOrb:not(#selfOrb):not([data-manage])"], ["wait", 400]],
|
|
50
51
|
"expect": { "dialog": true } },
|
|
51
52
|
{ "name": "Escape closes the card when no band is open", "continue": true,
|
|
52
53
|
"steps": [["press", "Escape"], ["wait", 150]],
|
|
@@ -59,11 +60,11 @@
|
|
|
59
60
|
"text": [["#cardBuilders", "11"], ["#cardShipped", "0"], ["#cardRel", "you build here"], ["#cardRuns", "amazonprimea.com"], ["#cardFaces .facesMore", "+6"], ["#cardHall", "Builders' hall"]],
|
|
60
61
|
"href": [["#cardHall", "https://builders.amazonprimea.com/"], ["#cardOpen", "https://amazonprimea.com/"]] } },
|
|
61
62
|
{ "name": "the ladder: a project that answered nothing is the card that shipped before (disc, name, tagline, address, listed, one door)", "continue": true,
|
|
62
|
-
"steps": [["press", "Escape"], ["wait", 150], ["dispatchClick", "#orbField a.worldOrb[data-origin=\"https://
|
|
63
|
+
"steps": [["press", "Escape"], ["wait", 150], ["dispatchClick", "#orbField a.worldOrb[data-origin=\"https://tidewater.cloudbongos.com\"]"], ["wait", 500]],
|
|
63
64
|
"expect": { "dialog": true, "hasClass": [["#projCard", "has-art", false]],
|
|
64
65
|
"hidden": ["#cardArt", "#cardFaces", "#cardFigs", "#cardBuildersFig", "#cardShippedFig", "#cardHall", "#cardRel", "#cardTypeL", "#cardOwnerL"],
|
|
65
66
|
"visible": ["#cardDot", "#cardName", "#cardTag", "#cardRuns", "#cardListed", "#cardOpen"],
|
|
66
|
-
"text": [["#cardRuns", "
|
|
67
|
+
"text": [["#cardRuns", "tidewater.cloudbongos.com"]] } },
|
|
67
68
|
{ "name": "the hall door is address-gated: a member of a project whose hall is its own site gets no door; the capped figure reads N+", "continue": true,
|
|
68
69
|
"steps": [["press", "Escape"], ["wait", 150], ["dispatchClick", "#orbField a.worldOrb[data-origin=\"https://emersonian-circles.cloudbongos.com\"]"], ["wait", 500]],
|
|
69
70
|
"expect": { "dialog": true, "hidden": ["#cardArt", "#cardFaces", "#cardHall"], "visible": ["#cardFigs", "#cardBuildersFig", "#cardShippedFig", "#cardOpen"],
|
|
@@ -74,7 +75,7 @@
|
|
|
74
75
|
"text": [["#cardRel", "yours"]], "href": [["#cardOpen", "/"]] } },
|
|
75
76
|
{ "name": "the table: the hall glyph rides the door cell on the one member row whose hall is elsewhere, and on the platform's row", "continue": true,
|
|
76
77
|
"steps": [["press", "Escape"], ["wait", 150], ["click", "#projTrig"], ["wait", 120], ["click", "a[data-sub=\"table\"]"], ["wait", 300]],
|
|
77
|
-
"expect": { "dialog": false, "visible": ["#selfHall"], "count": [["#mapRows tr[data-generated] .rowHall", 1], ["#mapRows tr[data-generated] td",
|
|
78
|
+
"expect": { "dialog": false, "visible": ["#selfHall"], "count": [["#mapRows tr[data-generated] .rowHall", 1], ["#mapRows tr[data-generated] td", 42]],
|
|
78
79
|
"href": [["#mapRows tr[data-generated] .rowHall", "https://builders.amazonprimea.com/"]] } },
|
|
79
80
|
{ "name": "back to the sky for the rest of the sequence", "continue": true,
|
|
80
81
|
"steps": [["click", "#projTrig"], ["wait", 120], ["click", "a[data-sub=\"sky\"]"], ["wait", 300]],
|
|
@@ -93,7 +94,7 @@
|
|
|
93
94
|
"expect": { "count": [["#menuProjects .bandCell:not([hidden])", 3], ["#menuProjects a[href=\"/docs\"]", 0]], "hidden": ["#menuProjects [data-nav=\"mine\"]"],
|
|
94
95
|
"href": [[".footNav a[href=\"/#about\"]", "/#about"], [".footNav a[href=\"/docs\"]", "/docs"]] } },
|
|
95
96
|
{ "name": "signed out: the card still closes on Escape (nothing throws with no session)", "continue": true,
|
|
96
|
-
"steps": [["press", "Escape"], ["wait", 100], ["dispatchClick", "#orbField a.worldOrb:not(#selfOrb)"], ["wait", 400], ["press", "Escape"], ["wait", 150]],
|
|
97
|
+
"steps": [["press", "Escape"], ["wait", 100], ["dispatchClick", "#orbField a.worldOrb:not(#selfOrb):not([data-manage])"], ["wait", 400], ["press", "Escape"], ["wait", 150]],
|
|
97
98
|
"expect": { "dialog": false, "closed": "#menuProjects" } },
|
|
98
99
|
{ "name": "390: the magnifier opens the row and focuses the field", "auth": true, "width": 390,
|
|
99
100
|
"steps": [["click", "#qToggle"], ["wait", 150]],
|
|
@@ -106,6 +107,30 @@
|
|
|
106
107
|
"expect": { "hasClass": [["#qWrap", "is-open", false]], "open": "#menuProjects", "top": [["#hubMain", 60]] } },
|
|
107
108
|
{ "name": "390: a solo Shift tap opens the fold and focuses the field, without overflow", "continue": true,
|
|
108
109
|
"steps": [["press", "Escape"], ["wait", 100], ["press", "Shift"], ["wait", 150]],
|
|
109
|
-
"expect": { "hasClass": [["#qWrap", "is-open", true]], "activeId": "mapQ", "noOverflow": true } }
|
|
110
|
+
"expect": { "hasClass": [["#qWrap", "is-open", true]], "activeId": "mapQ", "noOverflow": true } },
|
|
111
|
+
{ "name": "manage flows (task 1003345): the address fold on a live row records a domain", "auth": true, "width": 1440, "url": "/projects?view=mine",
|
|
112
|
+
"steps": [["wait",600],["click","li[data-inst=\"101\"] details.attachFold summary"],["fill","#dom-101","demo.example.com"],["click","[data-attach=\"101\"]"],["wait",600]],
|
|
113
|
+
"expect": {"text":[["#msg-101","Address recorded."]],"hasClass":[["#msg-101","ok",true]]} },
|
|
114
|
+
{ "name": "manage flows: restart on an unreachable row asks; the row re-reads the project and shows the queued rail, the door leaves while the intent is open", "auth": true, "width": 1440, "url": "/projects?view=mine",
|
|
115
|
+
"steps": [["wait",600],["click","[data-restart=\"105\"]"],["wait",900]],
|
|
116
|
+
"expect": {"text":[["li[data-inst=\"105\"] .strip .pnote","A restart is queued — this card updates when it starts."],["li[data-inst=\"105\"] .strip .stages li.now .slbl","Restart queued"]],"visible":["li[data-inst=\"105\"] .strip","li[data-inst=\"105\"] .worldOrb.ashen"],"hidden":["[data-restart=\"105\"]"]} },
|
|
117
|
+
{ "name": "manage flows: tear it down asks twice on a row — the truth first, then the door", "auth": true, "width": 1440, "url": "/projects?view=mine",
|
|
118
|
+
"steps": [["wait",600],["click","[data-teardown=\"101\"]"],["wait",200]],
|
|
119
|
+
"expect": {"visible":["li[data-inst=\"101\"] .confirmRow","[data-teardown-yes=\"101\"]","[data-teardown-no=\"101\"]"],"hidden":["[data-teardown=\"101\"]"]} },
|
|
120
|
+
{ "name": "manage flows: the second press queues the teardown; the row re-reads the project and shows the wind-down rail, the door leaves", "continue": true,
|
|
121
|
+
"steps": [["click","[data-teardown-yes=\"101\"]"],["wait",900]],
|
|
122
|
+
"expect": {"text":[["li[data-inst=\"101\"] .strip .pnote","A teardown is queued — this card updates when it starts."]],"visible":["li[data-inst=\"101\"] .strip.flowDown"],"hidden":["[data-teardown=\"101\"]","[data-teardown-yes=\"101\"]"]} },
|
|
123
|
+
{ "name": "manage flows: bring it back on a torn-down row re-files the project", "auth": true, "width": 1440, "url": "/projects?view=mine",
|
|
124
|
+
"steps": [["wait",600],["click","[data-revive=\"104\"]"],["wait",600]],
|
|
125
|
+
"expect": {"text":[["#msg-104","Queued. The platform picks it up from here."]],"hasClass":[["#msg-104","ok",true]],"visible":["li[data-inst=\"104\"] .worldOrb.ghost"]} },
|
|
126
|
+
{ "name": "manage flows: try again on a snagged row re-files it too", "auth": true, "width": 1440, "url": "/projects?view=mine",
|
|
127
|
+
"steps": [["wait",600],["click","[data-retry=\"103\"]"],["wait",600]],
|
|
128
|
+
"expect": {"text":[["#msg-103","Queued. The platform picks it up from here."]],"hasClass":[["#msg-103","ok",true]]} },
|
|
129
|
+
{ "name": "manage flows: how people join — pick a cell, save, the receipt lands under the choice", "auth": true, "width": 1440, "url": "/projects?view=manage&id=101",
|
|
130
|
+
"steps": [["wait",900],["check","input[name=\"mJoinPick\"][value=\"apply\"]"],["click","[data-join-save]"],["wait",600]],
|
|
131
|
+
"expect": {"text":[["#mJoinMsg","Saved. The project restarts to apply it."],["#mName","demo"]],"checked":"input[name=\"mJoinPick\"][value=\"apply\"]","visible":["#mOrb .worldOrb","#mFacts .ledger .k"]} },
|
|
132
|
+
{ "name": "manage flows: the ender asks for the name typed back, queues the teardown, and the page re-reads the project as queued", "auth": true, "width": 1440, "url": "/projects?view=manage&id=105",
|
|
133
|
+
"steps": [["wait",900],["click","[data-del-ask]"],["wait",200],["fill","#mDelName","mooring"],["wait",100],["click","[data-del-yes]"],["wait",900]],
|
|
134
|
+
"expect": {"text":[["#mDel .addr","A teardown is queued for this project."],["#mName","mooring"]],"hidden":["#mDelName","[data-del-ask]"],"visible":["#mStrip .strip.flowDown"]} }
|
|
110
135
|
]
|
|
111
136
|
}
|
|
@@ -1,28 +1,32 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_": "The hub's states for modules/ui-design/kit/render.js (task 1003320): name → url + actions + expectations. `auth` picks the signed-in stub. Rendered dark only at 1440/390/320 (the apex pins data-mode=\"dark\", task 1003336) and audited against the platform floors. The card states (task 1003301) walk the poster's ladder: the full poster, the artless two-up band, the all-null floor, the platform's own card.",
|
|
2
|
+
"_": "The hub's states for modules/ui-design/kit/render.js (task 1003320): name → url + actions + expectations. `auth` picks the signed-in stub. Rendered dark only at 1440/390/320 (the apex pins data-mode=\"dark\", task 1003336) and audited against the platform floors. The card states (task 1003301) walk the poster's ladder: the full poster, the artless two-up band, the all-null floor, the platform's own card. The signed-in stub OWNS Demo (provisioning-instances.json, task 1003345), and an owned orb's plain click opens its manage view, never its card — so the all-null floor card renders from projects-floor.states.json on the hub's own catalog (featured-hub.json, one un-owned null row more).",
|
|
3
3
|
"page": "/projects",
|
|
4
4
|
"surface": "public-landing",
|
|
5
5
|
"modes": ["dark"],
|
|
6
6
|
"modeQuery": false,
|
|
7
|
-
"ignoreRequests": ["/api/(gds|bongos)/me$", "/my-projects", "/provisioning/"],
|
|
8
|
-
"ignoreConsole": ["/api/(gds|bongos)/me", "\\b401\\b", "Unauthorized"],
|
|
7
|
+
"ignoreRequests": ["/api/(gds|bongos)/me$", "/my-projects", "/provisioning/instances/999", "/provisioning/instances/101$", "/api/gds/instance$"],
|
|
8
|
+
"ignoreConsole": ["/api/(gds|bongos)/me", "\\b401\\b", "\\b404\\b", "Unauthorized"],
|
|
9
9
|
"ignoreControls": [".skip", ".vh"],
|
|
10
10
|
"states": {
|
|
11
11
|
"out-map": { "auth": false, "expect": { "expanded": false } },
|
|
12
|
-
"in-map": { "auth": true, "expect": { "expanded": false, "count": [["#orbField .worldOrb[data-shape][data-style]",
|
|
12
|
+
"in-map": { "auth": true, "expect": { "expanded": false, "count": [["#orbField .worldOrb[data-shape][data-style]", 7]], "attr": [["#selfOrb", "data-template", "pair-chrome"], ["#orbField a.worldOrb[data-origin=\"https://amazonprimea.com\"]", "data-template", "bead-wash"], ["#orbField a.worldOrb[data-origin=\"https://demo.cloudbongos.com\"]", "data-shape", "pair"], ["#orbField a.worldOrb[data-origin=\"https://demo.cloudbongos.com\"]", "data-material", "gold"], ["#orbField a.worldOrb[data-origin=\"https://demo.cloudbongos.com\"]", "data-template", "pair-gold-wash"], ["#orbField a.worldOrb[data-origin=\"https://emersonian-circles.cloudbongos.com\"]", "data-template", "bead-obsidian-plain"]] } },
|
|
13
13
|
"in-map-projects": { "auth": true, "actions": [["click", "#projTrig"], ["wait", 350]], "expect": { "expanded": true, "open": "#menuProjects" } },
|
|
14
14
|
"in-map-profile": { "auth": true, "actions": [["click", "#who"], ["wait", 350]], "expect": { "expanded": true, "open": "#menuProfile" } },
|
|
15
15
|
"in-map-kbd": { "auth": true, "actions": [["focus", "#projTrig"], ["press", "Enter"], ["wait", 350], ["press", "ArrowDown"], ["wait", 120]], "expect": { "expanded": true, "activeText": "Table" } },
|
|
16
16
|
"in-map-search": { "auth": true, "actions": [["clickIfVisible", "#qToggle"], ["wait", 200], ["fill", "#mapQ", "emer"], ["wait", 300]], "expect": { "value": [["#mapQ", "emer"]] } },
|
|
17
17
|
"in-map-mine-on": { "auth": true, "actions": [["check", "#onlyMine"], ["wait", 300]], "expect": { "checked": "#onlyMine" } },
|
|
18
18
|
"in-table": { "auth": true, "actions": [["click", "#projTrig"], ["wait", 250], ["click", "a[data-sub=\"table\"]"], ["wait", 400]], "expect": { "visible": ["#sub-table-body", ".tblMast", ".mastObj", "#selfHall"], "closed": "#menuProjects", "count": [["#mapRows tr[data-generated] .rowHall", 1]] } },
|
|
19
|
-
"in-mine": { "auth": true, "url": "/projects?view=mine", "expect": { "hidden": ["#qWrap"] } },
|
|
19
|
+
"in-mine": { "auth": true, "url": "/projects?view=mine", "expect": { "hidden": ["#qWrap", "#empty"], "visible": ["#mineBar", "#projectsList", "#projectsList .proj .worldOrb .disc"], "count": [["#projectsList .proj", 5], ["#projectsList .proj.gone", 1], ["#projectsList .proj .worldOrb[data-shape][data-material][data-style]", 5], ["#projectsList .proj .strip", 1], ["#projectsList .proj .worldOrb.ashen", 2], ["#projectsList [data-restart]", 1]] } },
|
|
20
|
+
"in-manage": { "auth": true, "url": "/projects?view=manage&id=101", "expect": { "visible": ["#mMain", "#mChip .chip", "#mOrb .worldOrb", "#mFacts .ledger .k", "#mActs"], "hidden": ["#qWrap", "#mGate", "#mMissing", "#mErr"], "text": [["#mName", "demo"], ["#mTitle", "demo"]] } },
|
|
21
|
+
"in-manage-snag": { "auth": true, "url": "/projects?view=manage&id=103", "expect": { "visible": ["#mMain", "#mErr", "#mActs"], "text": [["#mName", "tidepool"]] } },
|
|
22
|
+
"in-manage-gone": { "auth": true, "url": "/projects?view=manage&id=104", "expect": { "visible": ["#mMain", "#mActs"], "hidden": ["#mErr"], "text": [["#mName", "old-lantern"]] } },
|
|
23
|
+
"in-manage-missing": { "auth": true, "url": "/projects?view=manage&id=999", "expect": { "visible": ["#mMissing"], "hidden": ["#mMain", "#mGate"] } },
|
|
24
|
+
"out-manage": { "auth": false, "url": "/projects?view=manage&id=101", "expect": { "visible": ["#mGate"], "hidden": ["#mMain", "#mMissing"] } },
|
|
20
25
|
"in-new": { "auth": true, "url": "/projects?view=new", "expect": { "hidden": ["#qWrap"] } },
|
|
21
26
|
"out-new": { "auth": false, "url": "/projects?view=new", "expect": { "hidden": ["#qWrap"] } },
|
|
22
27
|
"out-map-projects": { "auth": false, "actions": [["click", "#projTrig"], ["wait", 350]], "expect": { "expanded": true, "hidden": ["#menuProjects [data-nav=\"mine\"]"] } },
|
|
23
28
|
"card": { "auth": true, "actions": [["dispatchClick", "#orbField a.worldOrb[data-origin=\"https://amazonprimea.com\"]"], ["wait", 900]], "expect": { "dialog": true, "hasClass": [["#projCard", "has-art"]], "visible": ["#cardArt", "#cardFaces", "#cardBuildersFig", "#cardShippedFig", "#cardOpen", "#cardHall"], "text": [["#cardBuilders", "11"], ["#cardShipped", "0"], ["#cardRel", "you build here"], ["#cardRuns", "amazonprimea.com"], ["#cardFaces .facesMore", "+6"]], "count": [["#cardFaces img", 5]], "href": [["#cardHall", "https://builders.amazonprimea.com/"]] } },
|
|
24
29
|
"card-noart": { "auth": true, "actions": [["dispatchClick", "#orbField a.worldOrb[data-origin=\"https://emersonian-circles.cloudbongos.com\"]"], ["wait", 600]], "expect": { "dialog": true, "hasClass": [["#projCard", "has-art", false]], "hidden": ["#cardArt", "#cardFaces", "#cardHall"], "visible": ["#cardBuildersFig", "#cardShippedFig", "#cardOpen"], "text": [["#cardShipped", "30+"], ["#cardRel", "you build here"]] } },
|
|
25
|
-
"card-floor": { "auth": true, "actions": [["dispatchClick", "#orbField a.worldOrb[data-origin=\"https://demo.cloudbongos.com\"]"], ["wait", 600]], "expect": { "dialog": true, "hidden": ["#cardArt", "#cardFaces", "#cardFigs", "#cardHall", "#cardRel", "#cardTypeL", "#cardOwnerL"], "visible": ["#cardOpen", "#cardRuns", "#cardListed"] } },
|
|
26
30
|
"card-self": { "auth": true, "actions": [["dispatchClick", "#selfOrb"], ["wait", 600]], "expect": { "dialog": true, "hidden": ["#cardArt"], "visible": ["#cardBuildersFig", "#cardShippedFig", "#cardFaces", "#cardOpen", "#cardHall"], "text": [["#cardRel", "yours"]] } }
|
|
27
31
|
},
|
|
28
32
|
"reducedMotion": { "auth": true, "actions": [["click", "#projTrig"], ["wait", 100]] }
|
package/src/module-api.js
CHANGED
|
@@ -33,7 +33,7 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
33
33
|
// there. scripts/gds/bump-version.js still rewrites the literal below; it appends
|
|
34
34
|
// the entry to that file. Look for a version's history there, not here.
|
|
35
35
|
// ---------------------------------------------------------------------------
|
|
36
|
-
const CORE_VERSION = '1.19.
|
|
36
|
+
const CORE_VERSION = '1.19.421'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
37
37
|
|
|
38
38
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
39
39
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
|
@@ -284,16 +284,23 @@ test('the discoverable toggle names EXACTLY what it grants — the D3 sliver, na
|
|
|
284
284
|
'the section ships hidden — it reveals only where the hub module answers');
|
|
285
285
|
});
|
|
286
286
|
|
|
287
|
-
test('
|
|
287
|
+
test('each toggle claims exactly what is enforced — visibility in force, recruiting not yet', () => {
|
|
288
288
|
const js = readFileSync(join(ROOT, 'modules/hall-ui/public/settings.js'), 'utf8');
|
|
289
289
|
const html = readFileSync(join(ROOT, 'modules/hall-ui/public/settings.html'), 'utf8');
|
|
290
|
-
//
|
|
291
|
-
//
|
|
292
|
-
//
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
290
|
+
// A toggle may promise only what a read path actually enforces, and the two
|
|
291
|
+
// rows are at different stages. Task 1002967 wired VISIBILITY into every
|
|
292
|
+
// public read, so its row moved to the present tense here — and it must also
|
|
293
|
+
// say the page 404s to the OWNER, because that is the surprising half of D2
|
|
294
|
+
// (the owner reads their record through the own-scope side door instead).
|
|
295
|
+
// DISCOVERABILITY is still stored-only: no recruiting surface reads it, so a
|
|
296
|
+
// present-tense promise there would be the "protection you do not have" this
|
|
297
|
+
// assertion pair exists to prevent.
|
|
298
|
+
assert.match(js, /Private hides the page from everyone — including you/,
|
|
299
|
+
'visibility is enforced now, so the row says so — and names the 404-to-self');
|
|
300
|
+
assert.doesNotMatch(js, /Private will hide the page/,
|
|
301
|
+
'the future tense outlived its truth the moment resolveHandle joined the predicate');
|
|
302
|
+
assert.match(html, /profile page setting applies now[\s\S]*Recruiter discoverability is saved to your[\s\S]*starts applying as the recruiting surfaces land/,
|
|
303
|
+
'the section note splits the two: one in force, one recorded and waiting');
|
|
304
|
+
assert.doesNotMatch(js, /Recruiters looking for builders (?:can|are able to) find you\b/,
|
|
305
|
+
'no present-tense promise about recruiting reach — nothing reads that flag yet');
|
|
299
306
|
});
|