@bongos/core 1.21.176 → 1.21.177
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 +61 -361
- package/.claude/skills/planning-session/SKILL.md +2 -2
- package/config/scheduled-routines.json +3 -8
- package/docs/adr/0091-bounding-the-kernel-and-db-carve.md +1 -1
- package/docs/adr/0093-tranche-2-core-carve-sequence.md +1 -1
- package/docs/canonical-permissions.md +1 -1
- package/docs/file-map.md +3 -8
- package/docs/handoff-template.md +1 -1
- package/docs/module-api-changelog.md +2 -0
- package/docs/onboarding/diagrams/README.md +3 -7
- package/docs/onboarding/diagrams/assertions.json +3 -3
- package/docs/recipes/ops-gotchas.md +3 -1
- package/modules/autonomy/db.js +9 -0
- package/modules/autonomy/module.json +2 -2
- package/modules/autonomy/pollers/daily-checks.js +262 -0
- package/modules/autonomy/pollers/routine-timers.js +7 -5
- package/modules/autonomy/rank-roles.js +64 -0
- package/modules/autonomy/server-schedule.js +37 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +14 -0
- package/scripts/bongos/api-alias-caller-scan.js +2 -2
- package/scripts/bongos/audit-authorship.sh +0 -5
- package/scripts/bongos/audit-rank-roles.js +15 -56
- package/scripts/bongos/codemod-rename-scripts-gds.js +0 -1
- package/scripts/bongos/diagram-facts.js +1 -1
- package/scripts/bongos/do-api.js +1 -1
- package/scripts/bongos/fitness-checks-identity.js +4 -15
- package/scripts/bongos/gen-diagrams.js +3 -3
- package/scripts/bongos/pulse.js +5 -3
- package/scripts/bongos/routine-schedule.js +38 -3
- package/scripts/bongos/ship-regen.js +2 -3
- package/scripts/security/dep-audit.js +50 -124
- package/src/module-api.js +1 -1
- package/tests/autonomy_daily_checks.mjs +201 -0
- package/tests/claude_materialize.mjs +12 -12
- package/tests/effective_visibility_predicate.mjs +0 -18
- package/tests/routine_capture_kind.mjs +18 -7
- package/tests/routine_schedule.mjs +42 -0
- package/scripts/bongos/artifact-staleness.js +0 -267
- package/scripts/bongos/classify-discipline.js +0 -153
- package/scripts/bongos/codemod-rename-src-gds.js +0 -145
- package/scripts/bongos/coreB-carve-proof.sh +0 -725
- package/scripts/bongos/goal-names.js +0 -102
- package/scripts/bongos/rename-goals.js +0 -109
- package/scripts/bongos/seed-adopt-tasks.js +0 -75
- package/scripts/bongos/seed-api-contract-goal-tasks.js +0 -141
- package/scripts/bongos/seed-board-room-tasks.js +0 -196
- package/scripts/bongos/seed-bongos-app-tasks.js +0 -294
- package/scripts/bongos/seed-bongos-coreB-tranche1-tasks.js +0 -81
- package/scripts/bongos/seed-bongos-coreB-tranche2-tasks.js +0 -100
- package/scripts/bongos/seed-bongos-goal-hierarchy-tasks.js +0 -307
- package/scripts/bongos/seed-bongos-module-system-tasks.js +0 -346
- package/scripts/bongos/seed-bongos-tasks.js +0 -214
- package/scripts/bongos/seed-bongos-v1-seo-tasks.js +0 -104
- package/scripts/bongos/seed-bv1-community-tasks.js +0 -131
- package/scripts/bongos/seed-bv1-privacy-invites-tasks.js +0 -133
- package/scripts/bongos/seed-bv1-profile-page-tasks.js +0 -161
- package/scripts/bongos/seed-bv1-r01-delta-tasks.js +0 -154
- package/scripts/bongos/seed-bv2-tweak-mode-tasks.js +0 -277
- package/scripts/bongos/seed-gds-v4-tasks.js +0 -624
- package/scripts/bongos/seed-governance-tasks.js +0 -340
- package/scripts/bongos/seed-medusa-tasks.js +0 -440
- package/scripts/bongos/seed-module-upstreaming.js +0 -219
- package/scripts/bongos/seed-productization-tasks.js +0 -235
- package/scripts/bongos/seed-project-admin-console-tasks.js +0 -88
- package/scripts/bongos/seed-provisioning-tasks.js +0 -83
- package/scripts/bongos/seed-strict-versioning-tasks.js +0 -275
- package/scripts/bongos/seed-v2-world-tasks.js +0 -283
- package/scripts/gds/artifact-staleness.js +0 -7
- package/scripts/gds/classify-discipline.js +0 -7
- package/scripts/gds/codemod-rename-src-gds.js +0 -7
- package/scripts/gds/coreB-carve-proof.sh +0 -8
- package/scripts/gds/goal-names.js +0 -7
- package/scripts/gds/rename-goals.js +0 -7
- package/scripts/gds/seed-adopt-tasks.js +0 -7
- package/scripts/gds/seed-api-contract-goal-tasks.js +0 -7
- package/scripts/gds/seed-board-room-tasks.js +0 -6
- package/scripts/gds/seed-bongos-app-tasks.js +0 -7
- package/scripts/gds/seed-bongos-coreB-tranche1-tasks.js +0 -7
- package/scripts/gds/seed-bongos-coreB-tranche2-tasks.js +0 -7
- package/scripts/gds/seed-bongos-goal-hierarchy-tasks.js +0 -7
- package/scripts/gds/seed-bongos-module-system-tasks.js +0 -7
- package/scripts/gds/seed-bongos-tasks.js +0 -7
- package/scripts/gds/seed-bongos-v1-seo-tasks.js +0 -6
- package/scripts/gds/seed-bv1-community-tasks.js +0 -6
- package/scripts/gds/seed-bv1-privacy-invites-tasks.js +0 -6
- package/scripts/gds/seed-bv1-profile-page-tasks.js +0 -6
- package/scripts/gds/seed-bv1-r01-delta-tasks.js +0 -6
- package/scripts/gds/seed-bv2-tweak-mode-tasks.js +0 -6
- package/scripts/gds/seed-gds-v4-tasks.js +0 -7
- package/scripts/gds/seed-governance-tasks.js +0 -7
- package/scripts/gds/seed-medusa-tasks.js +0 -7
- package/scripts/gds/seed-module-upstreaming.js +0 -7
- package/scripts/gds/seed-productization-tasks.js +0 -7
- package/scripts/gds/seed-project-admin-console-tasks.js +0 -6
- package/scripts/gds/seed-provisioning-tasks.js +0 -7
- package/scripts/gds/seed-strict-versioning-tasks.js +0 -7
- package/scripts/gds/seed-v2-world-tasks.js +0 -7
- package/scripts/migrate_cost_ledger.sh +0 -178
- package/tests/artifact_staleness.mjs +0 -207
- package/tests/goal_names.mjs +0 -120
- package/tests/newcomer_generator.mjs +0 -48
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// modules/autonomy/rank-roles.js — which builders hold a rank the governance
|
|
4
|
+
// backfill cannot match? (task 1002873; moved here from
|
|
5
|
+
// scripts/bongos/audit-rank-roles.js in task 1004878 so the server's daily check and
|
|
6
|
+
// the CLI share ONE definition).
|
|
7
|
+
//
|
|
8
|
+
// Pure: no I/O, no core require. The live ladder is an ARGUMENT — the poller passes
|
|
9
|
+
// api.LIVE_RANK_LADDER, the CLI passes db-kernel's — because a module file reaches
|
|
10
|
+
// core only through the doorway.
|
|
11
|
+
|
|
12
|
+
// findRanksWithoutRoles — given roster rows and the live ladder, which builders hold
|
|
13
|
+
// a rank that `governance_002`'s INNER JOIN cannot match?
|
|
14
|
+
//
|
|
15
|
+
// A row with NO rank at all counts: NULL fails the INNER JOIN the same way a
|
|
16
|
+
// legacy value does, and reads as "fine" in every UI that renders a blank.
|
|
17
|
+
function findRanksWithoutRoles(rows, liveRanks) {
|
|
18
|
+
const live = new Set(liveRanks || []);
|
|
19
|
+
return (Array.isArray(rows) ? rows : [])
|
|
20
|
+
.filter((b) => b && !live.has(b.rank))
|
|
21
|
+
.map((b) => ({
|
|
22
|
+
id: b.id != null ? String(b.id) : null,
|
|
23
|
+
// The login is what an operator needs to act; it is already visible to any
|
|
24
|
+
// caller authorised to read the roster, so this adds no exposure.
|
|
25
|
+
github_login: b.github_login || null,
|
|
26
|
+
rank: b.rank === undefined ? null : b.rank,
|
|
27
|
+
status: b.status || null,
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// summarize — the rank histogram, so a CLEAN run still says what it looked at.
|
|
32
|
+
// A green check that prints nothing is indistinguishable from one that read
|
|
33
|
+
// nothing, which is the failure mode this whole check is about.
|
|
34
|
+
function summarize(rows) {
|
|
35
|
+
const counts = new Map();
|
|
36
|
+
for (const b of Array.isArray(rows) ? rows : []) {
|
|
37
|
+
const key = b && b.rank ? b.rank : '(none)';
|
|
38
|
+
counts.set(key, (counts.get(key) || 0) + 1);
|
|
39
|
+
}
|
|
40
|
+
return [...counts.entries()].sort((a, b) => b[1] - a[1]);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ideaFor — the one idea a finding becomes, filed by either caller.
|
|
44
|
+
function ideaFor(offenders, { filedBy = 'scripts/bongos/audit-rank-roles.js' } = {}) {
|
|
45
|
+
return {
|
|
46
|
+
title: `[rank-roles] ${offenders.length} builder(s) on a rank with no governance role`,
|
|
47
|
+
bodyMd: [
|
|
48
|
+
`${offenders.length} builder(s) hold a rank with no matching governance_roles row.`,
|
|
49
|
+
'',
|
|
50
|
+
'They resolve to ZERO permissions — admitted by rank threshold under requireRank,',
|
|
51
|
+
'refused by the permission resolver, and given no reason (ADR 0151 fail-closed).',
|
|
52
|
+
'',
|
|
53
|
+
...offenders.map((o) => `- builder ${o.id}${o.github_login ? ` (${o.github_login})` : ''} rank=${o.rank === null ? '(none)' : o.rank} status=${o.status || '?'}`),
|
|
54
|
+
'',
|
|
55
|
+
'Fix: move each to a live rank through the normal rank-change path, which rotates',
|
|
56
|
+
'the governance role in the same transaction. Not auto-repaired: changing a rank is',
|
|
57
|
+
'an authority decision, and a sweep is the wrong actor for it.',
|
|
58
|
+
'',
|
|
59
|
+
`Filed by ${filedBy} (task 1002873).`,
|
|
60
|
+
].join('\n'),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = { findRanksWithoutRoles, summarize, ideaFor };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// modules/autonomy/server-schedule.js — which routines the always-on server fires
|
|
4
|
+
// itself, and how often (task 1004878).
|
|
5
|
+
//
|
|
6
|
+
// ONE TABLE, TWO READERS THAT MUST AGREE. The pollers in ./pollers/ take their
|
|
7
|
+
// periods from here, and scripts/bongos/routine-schedule.js reads the same rows to
|
|
8
|
+
// decide whether a routine's declared `cadence:` has something that fires it. Before
|
|
9
|
+
// this table the only scheduler that check could see was .github/workflows/, so a
|
|
10
|
+
// routine fired in-process (peer-vote-tally) read as "manual only", and the routines
|
|
11
|
+
// nothing fired at all read the same way — the two cases were indistinguishable.
|
|
12
|
+
//
|
|
13
|
+
// Deliberately dependency-free: the merge-gate check requires this file in a plain
|
|
14
|
+
// checkout with no database and no module loader.
|
|
15
|
+
|
|
16
|
+
const DAY_MS = 24 * 60 * 60_000;
|
|
17
|
+
const WEEK_MS = 7 * DAY_MS;
|
|
18
|
+
|
|
19
|
+
// The cadence names a server row may use, and the period each one means. A subset of
|
|
20
|
+
// routine-schedule.js's vocabulary: nothing in-process runs more often than daily.
|
|
21
|
+
const CADENCE_MS = Object.freeze({ daily: DAY_MS, weekly: WEEK_MS });
|
|
22
|
+
|
|
23
|
+
const SERVER_ROUTINES = Object.freeze([
|
|
24
|
+
{ routine: 'peer-vote-tally', cadence: 'weekly', poller: 'routine-timers' },
|
|
25
|
+
{ routine: 'rank-roles-audit', cadence: 'daily', poller: 'daily-checks' },
|
|
26
|
+
{ routine: 'dep-audit-nightly', cadence: 'daily', poller: 'daily-checks' },
|
|
27
|
+
].map(Object.freeze));
|
|
28
|
+
|
|
29
|
+
// periodMs('rank-roles-audit') → 86400000. Throws on a routine the table does not
|
|
30
|
+
// carry, so a poller can never quietly fall back to a period nobody declared.
|
|
31
|
+
function periodMs(routine) {
|
|
32
|
+
const row = SERVER_ROUTINES.find((r) => r.routine === routine);
|
|
33
|
+
if (!row) throw new Error(`server-schedule: '${routine}' is not a server-fired routine`);
|
|
34
|
+
return CADENCE_MS[row.cadence];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = { SERVER_ROUTINES, CADENCE_MS, periodMs, DAY_MS, WEEK_MS };
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.177",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.21.
|
|
9
|
+
"version": "1.21.177",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.177",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/release-notes.json
CHANGED
|
@@ -10308,5 +10308,19 @@
|
|
|
10308
10308
|
"id": "1004858",
|
|
10309
10309
|
"text": "The status page no longer shows an uptime panel and a \"standing\" card that only ever said \"unknown\" — the checker behind them was never switched on. The dead checker and its data feed are gone too."
|
|
10310
10310
|
}
|
|
10311
|
+
],
|
|
10312
|
+
"1.21.177": [
|
|
10313
|
+
{
|
|
10314
|
+
"id": "1004887",
|
|
10315
|
+
"text": "Main was failing one safety check after two cleanup changes landed together. The check's expected count now matches the files we deliberately removed, so main is green again."
|
|
10316
|
+
},
|
|
10317
|
+
{
|
|
10318
|
+
"id": "1004878",
|
|
10319
|
+
"text": "Two daily safety checks — builders on a broken rank and known-vulnerable dependencies — now run on the server by themselves and land in the ideas inbox; five dead routines were retired."
|
|
10320
|
+
},
|
|
10321
|
+
{
|
|
10322
|
+
"id": "1004856",
|
|
10323
|
+
"text": "Removed 30 old scripts that each did one job once (mostly creating batches of tasks) and were never used again — about 6,500 lines less to read and maintain. Nothing that runs today used them."
|
|
10324
|
+
}
|
|
10311
10325
|
]
|
|
10312
10326
|
}
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
// Note fitness-ratchets.js belongs here too: it is the GATE, and its builder-facing hint has
|
|
37
37
|
// to spell the alias out to tell anyone what they tripped over.
|
|
38
38
|
// 4. THE RENAME GUARDRAILS (path-excluded). rename-history-check.js, rename-ledger-prose.js,
|
|
39
|
-
// gds-literal-scan.js,
|
|
39
|
+
// gds-literal-scan.js, fitness-ratchets.js and THIS FILE name the
|
|
40
40
|
// old path because that is what they are for. Same principle as GDS_RULE_FILES next door: a
|
|
41
41
|
// gate that cannot state its own rule cannot explain itself.
|
|
42
42
|
// 5. tests/ (path-excluded). Tests are what keeps the alias exercised in CI. Moving them off it
|
|
@@ -110,7 +110,7 @@ const ALIAS_EXCLUDED_PATHS = [
|
|
|
110
110
|
// 3. the live proof that the alias still answers
|
|
111
111
|
/^scripts\/(?:gds|bongos)\/smoke-gds\.js$/,
|
|
112
112
|
// 4. the rename guardrails — they name the old path on purpose
|
|
113
|
-
/^scripts\/(?:gds|bongos)\/(rename-history-check|rename-ledger-prose|gds-literal-scan|
|
|
113
|
+
/^scripts\/(?:gds|bongos)\/(rename-history-check|rename-ledger-prose|gds-literal-scan|api-alias-caller-scan|fitness-ratchets)\.js$/,
|
|
114
114
|
/^config\/(fitness-baselines|rename-history-baseline)\.json$/,
|
|
115
115
|
// 5. tests keep the alias exercised in CI — the repo's own two spellings, `tests/` at the root
|
|
116
116
|
// and a module's `tests/` or `test/` dir. Deliberately NOT a blanket `\.mjs$`: three .mjs
|
|
@@ -196,11 +196,6 @@ ALLOWLIST=(
|
|
|
196
196
|
# instance's owner literal precisely so packaging can SCRUB it from every
|
|
197
197
|
# published core — the anti-leak spec, not a runtime hardcode (task 1002640).
|
|
198
198
|
'scripts/bongos/package-core.js'
|
|
199
|
-
|
|
200
|
-
# Frozen seed script — a task DESCRIPTION quotes migration 023's founding-
|
|
201
|
-
# Archon literal as the thing that task exists to remove (the
|
|
202
|
-
# seed-v3-tasks.js class: stripping it would lose the task history).
|
|
203
|
-
'scripts/bongos/seed-medusa-tasks.js'
|
|
204
199
|
)
|
|
205
200
|
|
|
206
201
|
# ----------------------------------------------------------------------------
|
|
@@ -21,8 +21,10 @@
|
|
|
21
21
|
// THIS IS THE RE-RUNNABLE HALF, and it is deliberately NOT fatal at boot. Idea
|
|
22
22
|
// 1000622 records that trap: an assertion that throws on startup turns a data
|
|
23
23
|
// problem on one instance into an outage. This is a standalone read-only script
|
|
24
|
-
// for a
|
|
25
|
-
// in the server's boot path requires it.
|
|
24
|
+
// for a by-hand or deploy-time caller — it exits 1 so a caller can act, and nothing
|
|
25
|
+
// in the server's boot path requires it. The SCHEDULED run is in-process: the
|
|
26
|
+
// server's daily-checks poller (task 1004878) reads the roster straight from its
|
|
27
|
+
// database and files the same idea, so it needs no CLI session.
|
|
26
28
|
//
|
|
27
29
|
// It reports rather than repairs, and that is a decision, not an omission:
|
|
28
30
|
// rewriting somebody's rank is an authority change, and it belongs to an operator
|
|
@@ -36,7 +38,7 @@
|
|
|
36
38
|
// 2 could not read the roster (no session / network / route absent)
|
|
37
39
|
//
|
|
38
40
|
// `--file-idea` is what makes this a SURFACE rather than another thing nobody
|
|
39
|
-
// runs —
|
|
41
|
+
// runs — spawning capture.js so idea
|
|
40
42
|
// filing has one implementation. Without it a nightly deterministic run that exits
|
|
41
43
|
// 1 reports into a log nobody reads, which is the exact criticism this task makes
|
|
42
44
|
// of the status quo.
|
|
@@ -48,69 +50,26 @@ const os = require('node:os');
|
|
|
48
50
|
const { cliClient, requireSession, cliExit } = require('./cli-lib');
|
|
49
51
|
const { LIVE_RANK_LADDER } = require('../../src/bongos/db-kernel.js');
|
|
50
52
|
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
// gate has no Postgres, which is exactly why the gap this script closes went
|
|
56
|
-
// unverified for so long. A DB-free test of the decision is not a consolation
|
|
57
|
-
// prize here — it is the thing that can actually run in CI.
|
|
58
|
-
//
|
|
59
|
-
// A row with NO rank at all counts: NULL fails the INNER JOIN the same way a
|
|
60
|
-
// legacy value does, and reads as "fine" in every UI that renders a blank.
|
|
61
|
-
function findRanksWithoutRoles(rows, liveRanks = LIVE_RANK_LADDER) {
|
|
62
|
-
const live = new Set(liveRanks);
|
|
63
|
-
return (Array.isArray(rows) ? rows : [])
|
|
64
|
-
.filter((b) => b && !live.has(b.rank))
|
|
65
|
-
.map((b) => ({
|
|
66
|
-
id: b.id != null ? String(b.id) : null,
|
|
67
|
-
// The login is what an operator needs to act; it is already visible to any
|
|
68
|
-
// caller authorised to read the roster, so this adds no exposure.
|
|
69
|
-
github_login: b.github_login || null,
|
|
70
|
-
rank: b.rank === undefined ? null : b.rank,
|
|
71
|
-
status: b.status || null,
|
|
72
|
-
}));
|
|
73
|
-
}
|
|
53
|
+
// The pure half lives in modules/autonomy/rank-roles.js (task 1004878), shared with
|
|
54
|
+
// the server's daily check (modules/autonomy/pollers/daily-checks.js) so the two
|
|
55
|
+
// cannot disagree about what an offender is. This wrapper defaults the ladder.
|
|
56
|
+
const rankRoles = require('../../modules/autonomy/rank-roles.js');
|
|
74
57
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
// nothing, which is the failure mode this whole task is about.
|
|
78
|
-
function summarize(rows) {
|
|
79
|
-
const counts = new Map();
|
|
80
|
-
for (const b of Array.isArray(rows) ? rows : []) {
|
|
81
|
-
const key = b && b.rank ? b.rank : '(none)';
|
|
82
|
-
counts.set(key, (counts.get(key) || 0) + 1);
|
|
83
|
-
}
|
|
84
|
-
return [...counts.entries()].sort((a, b) => b[1] - a[1]);
|
|
85
|
-
}
|
|
58
|
+
const findRanksWithoutRoles = (rows, liveRanks = LIVE_RANK_LADDER) => rankRoles.findRanksWithoutRoles(rows, liveRanks);
|
|
59
|
+
const { summarize } = rankRoles;
|
|
86
60
|
|
|
87
61
|
// Spawn capture.js rather than POSTing here, so idea filing keeps one
|
|
88
|
-
// implementation
|
|
62
|
+
// implementation. Best-effort: a failure to
|
|
89
63
|
// file must not turn a successful audit into a crash — the exit code and stdout
|
|
90
64
|
// still carry the finding.
|
|
91
65
|
function fileIdea(offenders) {
|
|
92
|
-
const
|
|
93
|
-
`${offenders.length} builder(s) hold a rank with no matching governance_roles row.`,
|
|
94
|
-
'',
|
|
95
|
-
'They resolve to ZERO permissions — admitted by rank threshold under requireRank,',
|
|
96
|
-
'refused by the permission resolver, and given no reason (ADR 0151 fail-closed).',
|
|
97
|
-
'',
|
|
98
|
-
...offenders.map((o) => `- builder ${o.id}${o.github_login ? ` (${o.github_login})` : ''} rank=${o.rank === null ? '(none)' : o.rank} status=${o.status || '?'}`),
|
|
99
|
-
'',
|
|
100
|
-
'Fix: move each to a live rank through the normal rank-change path, which rotates',
|
|
101
|
-
'the governance role in the same transaction. Not auto-repaired: changing a rank is',
|
|
102
|
-
'an authority decision, and a sweep is the wrong actor for it.',
|
|
103
|
-
'',
|
|
104
|
-
'Filed by scripts/bongos/audit-rank-roles.js (task 1002873).',
|
|
105
|
-
].join('\n');
|
|
66
|
+
const { title, bodyMd } = rankRoles.ideaFor(offenders);
|
|
106
67
|
const tmp = path.join(os.tmpdir(), `rank-roles-audit-${process.pid}.md`);
|
|
107
68
|
try {
|
|
108
|
-
fs.writeFileSync(tmp,
|
|
69
|
+
fs.writeFileSync(tmp, bodyMd, 'utf8');
|
|
109
70
|
const r = spawnSync(
|
|
110
71
|
process.execPath,
|
|
111
|
-
[path.join(__dirname, 'capture.js'),
|
|
112
|
-
`[rank-roles] ${offenders.length} builder(s) on a rank with no governance role`,
|
|
113
|
-
'--kind', 'bug', '--body-file', tmp],
|
|
72
|
+
[path.join(__dirname, 'capture.js'), title, '--kind', 'bug', '--body-file', tmp],
|
|
114
73
|
{ encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 60_000 }
|
|
115
74
|
);
|
|
116
75
|
if (r.status !== 0) console.error('audit-rank-roles: capture.js failed to file the idea — the exit code above still carries the finding.');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Shared fact-extractor for the onboarding diagrams (task #357).
|
|
2
2
|
//
|
|
3
3
|
// Single source of truth for "what the live system actually is", used by BOTH:
|
|
4
|
-
// - the drift checker
|
|
4
|
+
// - (formerly) the drift checker .claude/scheduled-tasks/diagram-drift/check.js, retired in task 1004878, which
|
|
5
5
|
// compares these facts against assertions.json and nudges on divergence; and
|
|
6
6
|
// - the generator (scripts/bongos/gen-diagrams.js), which regenerates the
|
|
7
7
|
// mechanical diagrams (.mmd) FROM these facts on every deploy.
|
package/scripts/bongos/do-api.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// logs the call it WOULD make and returns a synthetic, clearly-marked
|
|
16
16
|
// result (`{ dryRun: true, ... }`) so the orchestrator's control flow runs
|
|
17
17
|
// end-to-end against a throwaway plan. This mirrors how provision-staging.sh
|
|
18
|
-
//
|
|
18
|
+
// ships runnable logic whose LIVE leg is a separate,
|
|
19
19
|
// explicit operator action.
|
|
20
20
|
// - Live mode requires a token (DO_API_TOKEN); the constructor throws clearly
|
|
21
21
|
// if asked to run live without one — it never silently no-ops a real call.
|
|
@@ -79,10 +79,8 @@ const IDENTITY_SCAN_EXTRA_FILES = [];
|
|
|
79
79
|
// * backfill-sessions.js / version-close.js — one-time operator runbook text
|
|
80
80
|
// naming the actual prod DB + a historical local directory. Describing what
|
|
81
81
|
// an operator must literally type is the correct content.
|
|
82
|
-
// * seed-*.js — the
|
|
83
|
-
//
|
|
84
|
-
// falsify the record; deleting the files would destroy it. They are inert
|
|
85
|
-
// (idempotent on source_ref) and referenced only by generated manifests.
|
|
82
|
+
// * seed-*.js — the nine one-shot seeders once listed here (prose inside seeded
|
|
83
|
+
// task descriptions) were deleted by task 1004856; the repo history keeps them.
|
|
86
84
|
// * the four *.sh entries — surfaced when `.sh` joined the extension set (task
|
|
87
85
|
// 1002567). Each is a HOST-PINNED OPS script, not portable code: they name the
|
|
88
86
|
// live box's actual Postgres database/role and its actual /etc secrets dir, and
|
|
@@ -94,7 +92,7 @@ const IDENTITY_SCAN_EXTRA_FILES = [];
|
|
|
94
92
|
// NEW literal in any of them fails exactly like one in an unlisted file.
|
|
95
93
|
// * fitness-checks-identity.js / package-core.js — THE DETECTORS THEMSELVES: Check 13's regexes
|
|
96
94
|
// and package-core's PRIOR_INSTANCE_FLOOR name the brand because hunting it is
|
|
97
|
-
// their job.
|
|
95
|
+
// their job. (seed-v3-tasks.js was the third; it is gone.) All
|
|
98
96
|
// three passed pre-1002756 only by the old stripper's accidents (phantom
|
|
99
97
|
// blocks + regex-blindness); now they are seen and counted.
|
|
100
98
|
const IDENTITY_SCAN_ALLOWED = {
|
|
@@ -113,19 +111,10 @@ const IDENTITY_SCAN_ALLOWED = {
|
|
|
113
111
|
'scripts/bongos/seed-staging.sh': 4,
|
|
114
112
|
'scripts/bongos/backfill-sessions.js': 2,
|
|
115
113
|
'scripts/bongos/publish-manifest.js': 3,
|
|
116
|
-
'scripts/bongos/seed-bongos-coreB-tranche1-tasks.js': 1,
|
|
117
|
-
'scripts/bongos/seed-bongos-coreB-tranche2-tasks.js': 1,
|
|
118
|
-
'scripts/bongos/seed-bongos-goal-hierarchy-tasks.js': 1,
|
|
119
|
-
'scripts/bongos/seed-bongos-tasks.js': 1,
|
|
120
|
-
'scripts/bongos/seed-gds-v4-tasks.js': 1,
|
|
121
|
-
'scripts/bongos/seed-medusa-tasks.js': 7,
|
|
122
|
-
'scripts/bongos/seed-module-upstreaming.js': 1,
|
|
123
|
-
'scripts/bongos/seed-productization-tasks.js': 6,
|
|
124
|
-
'scripts/bongos/seed-provisioning-tasks.js': 2,
|
|
125
114
|
'scripts/bongos/version-close.js': 1,
|
|
126
115
|
// task 1002853 (the company literal joined the list): audit-authorship.sh is itself a
|
|
127
116
|
// detector — its ops allowlist names the founder's commit address so it can exempt it; grade-smoke.js
|
|
128
|
-
// is a protected grader probe whose fixture task prose names the company
|
|
117
|
+
// is a protected grader probe whose fixture task prose names the company.
|
|
129
118
|
'scripts/bongos/audit-authorship.sh': 1,
|
|
130
119
|
'scripts/bongos/grade-smoke.js': 1,
|
|
131
120
|
};
|
|
@@ -488,7 +488,7 @@ function rebuildAssertions(prev, f) {
|
|
|
488
488
|
next.architecture = {
|
|
489
489
|
diagram: '04-architecture',
|
|
490
490
|
_comment:
|
|
491
|
-
"Pieces of 04-architecture gated on a backing Bongos task. A piece is LIVE iff its task is 'shipped'; live_tasks must ALL be shipped, pending_tasks must NOT yet be. Rebuilt by scripts/bongos/gen-diagrams.js in lockstep with the diagram
|
|
491
|
+
"Pieces of 04-architecture gated on a backing Bongos task. A piece is LIVE iff its task is 'shipped'; live_tasks must ALL be shipped, pending_tasks must NOT yet be. Rebuilt by scripts/bongos/gen-diagrams.js in lockstep with the diagram. The machine/server/GitHub topology is structural (not task-gated); these are the LIVE/PLANNED markers. MEMORY layer (ADR 0024): 273 schema / 274 sync-on-ship / 275 pull-on-start / 276 isolation. Ship flow: 1025 (server-mediated publish, for a checkout with no push credential), 1028 (GitHub App push credential). The deploy leg is the standalone AUTO-PULL (ADR 0108/0129), not a task-gated GitHub-Actions CI job, so 859 is intentionally absent.",
|
|
492
492
|
live_tasks: arch.live_tasks,
|
|
493
493
|
pending_tasks: arch.pending_tasks,
|
|
494
494
|
};
|
|
@@ -499,7 +499,7 @@ function rebuildAssertions(prev, f) {
|
|
|
499
499
|
...next.drachmae_karma,
|
|
500
500
|
diagram: '03-drachmae-karma',
|
|
501
501
|
_comment:
|
|
502
|
-
"Pieces of 03-drachmae-karma that are gated on a backing Bongos task. A piece is LIVE iff its task is 'shipped'. live_tasks must ALL be shipped live; pending_tasks must NOT yet be shipped. Rebuilt automatically by scripts/bongos/gen-diagrams.js from the authenticated /tasks/:id status; the diagram is regenerated in lockstep
|
|
502
|
+
"Pieces of 03-drachmae-karma that are gated on a backing Bongos task. A piece is LIVE iff its task is 'shipped'. live_tasks must ALL be shipped live; pending_tasks must NOT yet be shipped. Rebuilt automatically by scripts/bongos/gen-diagrams.js from the authenticated /tasks/:id status; the diagram is regenerated in lockstep. D_SHIP (credit_log trigger) and D_ACH (achievement bonuses) have no task gate and are always LIVE. achievements_seeded mirrors 03's 'N seeded' label, counted from the achievements seed INSERTs in migrations/.",
|
|
503
503
|
};
|
|
504
504
|
next.drachmae_karma.live_tasks = dk.live_tasks;
|
|
505
505
|
next.drachmae_karma.pending_tasks = dk.pending_tasks;
|
|
@@ -589,7 +589,7 @@ const CONCEPTUAL_TARGETS = [
|
|
|
589
589
|
// r.error (ENOENT) and r.status is null. The .mmd sources are already
|
|
590
590
|
// written above and the caller (ship.js regenerateDiagrams) will commit
|
|
591
591
|
// them; PNGs get re-rendered on the Linux droplet at deploy time, and the
|
|
592
|
-
//
|
|
592
|
+
// next render-capable ship refreshes them. So skip the render
|
|
593
593
|
// NON-FATALLY rather than hard-failing the whole ship on a missing bash.
|
|
594
594
|
console.warn(
|
|
595
595
|
`gen-diagrams: could not run the render step (${r.error.code || r.error.message}). ` +
|
package/scripts/bongos/pulse.js
CHANGED
|
@@ -8,9 +8,11 @@
|
|
|
8
8
|
// node scripts/bongos/pulse.js --claim-id <N>
|
|
9
9
|
//
|
|
10
10
|
// 2) Release-stale mode (V3.R49 / #268) — scan for active claims with
|
|
11
|
-
// no pulse in N hours and auto-release them.
|
|
12
|
-
// `stale-claim-release`
|
|
13
|
-
//
|
|
11
|
+
// no pulse in N hours and auto-release them. Runnable by hand for ops
|
|
12
|
+
// ONLY: the scheduled `stale-claim-release` routine was retired unscheduled
|
|
13
|
+
// in task 1004878, because nothing records pulses yet (no POST /pulse route,
|
|
14
|
+
// no hook sends one), so a timed sweep would release every claim older
|
|
15
|
+
// than the threshold.
|
|
14
16
|
// node scripts/bongos/pulse.js --release-stale [--threshold-hours 6] [--dry-run]
|
|
15
17
|
//
|
|
16
18
|
// -------------------------------------------------------------------------
|
|
@@ -51,6 +51,9 @@ const TASKS_DIR = path.join(ROOT, '.claude', 'scheduled-tasks');
|
|
|
51
51
|
const WORKFLOWS_DIR = path.join(ROOT, '.github', 'workflows');
|
|
52
52
|
|
|
53
53
|
const { parseRoutineFrontmatter } = require('./run-routine.js');
|
|
54
|
+
// The routines the always-on server fires itself (task 1004878) — the second
|
|
55
|
+
// scheduler this check reads, beside .github/workflows/.
|
|
56
|
+
const { SERVER_ROUTINES } = require('../../modules/autonomy/server-schedule.js');
|
|
54
57
|
|
|
55
58
|
// The NAMED cadences a routine may declare. The value is the period a cron
|
|
56
59
|
// expression must resolve to for the two to agree. Add a name here and
|
|
@@ -189,17 +192,32 @@ function readWorkflows(dir = WORKFLOWS_DIR) {
|
|
|
189
192
|
// `workflows === null` means "this checkout has no CI to schedule with", which
|
|
190
193
|
// downgrades unscheduled routines from violations to warnings (see the two-repo
|
|
191
194
|
// note in the header).
|
|
192
|
-
|
|
195
|
+
//
|
|
196
|
+
// `server` is the in-process scheduler (modules/autonomy/server-schedule.js): rows
|
|
197
|
+
// of { routine, cadence } the always-on server fires itself. A row there IS a
|
|
198
|
+
// scheduler, at exactly its cadence — and it travels with the core, so it fires in
|
|
199
|
+
// an instance checkout too, where there are no workflows. It is held to the same
|
|
200
|
+
// rules as a cron: the routine must declare the cadence the server realizes, and
|
|
201
|
+
// must still exist.
|
|
202
|
+
function evaluate({ withCadence, withoutCadence }, workflows, server = []) {
|
|
193
203
|
const violations = [];
|
|
194
204
|
const warnings = [];
|
|
195
205
|
const hasCI = workflows !== null;
|
|
196
206
|
const wf = workflows || [];
|
|
207
|
+
const serverRow = new Map(server.map((s) => [s.routine, s]));
|
|
197
208
|
|
|
198
209
|
for (const r of withCadence) {
|
|
199
210
|
if (!isCadence(r.cadence)) {
|
|
200
211
|
violations.push(`routine '${r.name}': unknown cadence "${r.cadence}" (want ${[...CADENCES].join('|')}, or <N>min where N divides 60)`);
|
|
201
212
|
continue;
|
|
202
213
|
}
|
|
214
|
+
const srv = serverRow.get(r.name);
|
|
215
|
+
if (srv) {
|
|
216
|
+
if (srv.cadence !== r.cadence) {
|
|
217
|
+
violations.push(`routine '${r.name}' declares cadence: ${r.cadence} but the server fires it ${srv.cadence} (modules/autonomy/server-schedule.js)`);
|
|
218
|
+
}
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
203
221
|
const firing = wf.filter((w) => invokesRoutine(w.text, r.name, r.steps));
|
|
204
222
|
if (!firing.length) {
|
|
205
223
|
const msg = `routine '${r.name}' declares cadence: ${r.cadence} but NOTHING fires it — no scheduled workflow invokes it`;
|
|
@@ -242,7 +260,24 @@ function evaluate({ withCadence, withoutCadence }, workflows) {
|
|
|
242
260
|
}
|
|
243
261
|
violations.push(...dangling);
|
|
244
262
|
|
|
263
|
+
// The server-side rename rot, and a label that lies the other way: a routine the
|
|
264
|
+
// server fires on a schedule its own SKILL.md says nothing about. Skipped where
|
|
265
|
+
// there is no routine tree at all (an instance before materialization) — there
|
|
266
|
+
// every row would read as dangling, and none of them is.
|
|
267
|
+
const serverFired = new Set();
|
|
268
|
+
if (known.size) {
|
|
269
|
+
for (const s of server) {
|
|
270
|
+
if (!known.has(s.routine)) {
|
|
271
|
+
violations.push(`modules/autonomy/server-schedule.js fires routine '${s.routine}', which has no .claude/scheduled-tasks/${s.routine}/SKILL.md`);
|
|
272
|
+
} else if (withoutCadence.some((r) => r.name === s.routine)) {
|
|
273
|
+
violations.push(`routine '${s.routine}' is fired ${s.cadence} by the server but its SKILL.md declares no cadence: — add \`cadence: ${s.cadence}\``);
|
|
274
|
+
serverFired.add(s.routine);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
245
279
|
for (const r of withoutCadence) {
|
|
280
|
+
if (serverFired.has(r.name)) continue;
|
|
246
281
|
warnings.push(`routine '${r.name}' declares no cadence: — it can only ever be run by hand`);
|
|
247
282
|
}
|
|
248
283
|
return { violations, warnings };
|
|
@@ -252,7 +287,7 @@ function evaluate({ withCadence, withoutCadence }, workflows) {
|
|
|
252
287
|
function checkRoutineCadencesScheduled() {
|
|
253
288
|
const routines = readRoutines();
|
|
254
289
|
const workflows = readWorkflows();
|
|
255
|
-
const { violations, warnings } = evaluate(routines, workflows);
|
|
290
|
+
const { violations, warnings } = evaluate(routines, workflows, SERVER_ROUTINES);
|
|
256
291
|
const scheduled = routines.withCadence.length - violations.filter((v) => v.startsWith("routine '")).length;
|
|
257
292
|
return {
|
|
258
293
|
name: 'a declared cron cadence has something that fires it (task 1003209)',
|
|
@@ -262,7 +297,7 @@ function checkRoutineCadencesScheduled() {
|
|
|
262
297
|
warnings,
|
|
263
298
|
note: violations.length
|
|
264
299
|
? 'a scheduled routine is declared but dormant — add/repair the scheduler, or drop the cadence: field and stop claiming it runs'
|
|
265
|
-
: `${scheduled}/${routines.withCadence.length} cadence-declaring routine(s) fired by a committed scheduler at the declared period; ${routines.withoutCadence.length} manual-only.`,
|
|
300
|
+
: `${scheduled}/${routines.withCadence.length} cadence-declaring routine(s) fired by a committed scheduler (a workflow or the server) at the declared period; ${routines.withoutCadence.length} manual-only.`,
|
|
266
301
|
};
|
|
267
302
|
}
|
|
268
303
|
|
|
@@ -22,7 +22,7 @@ function currentBranch() {
|
|
|
22
22
|
// Task #357: keep the 3 mechanical onboarding diagrams current on every deploy.
|
|
23
23
|
// Regenerate them from live facts; if any changed, commit the refresh on this
|
|
24
24
|
// branch so it ships with the deploy. Non-fatal — a render/network hiccup must
|
|
25
|
-
// never block a ship; the
|
|
25
|
+
// never block a ship; the next render-capable ship refreshes any miss.
|
|
26
26
|
//
|
|
27
27
|
// Returns a status string ('skipped-server' | 'regen-failed' | 'current' |
|
|
28
28
|
// 'committed' | 'committed-nofatal') so the regression test (task 1046) can
|
|
@@ -35,8 +35,7 @@ function regenerateDiagrams() {
|
|
|
35
35
|
// leaving those files UNCOMMITTED in the builder's tree (had to be reverted by
|
|
36
36
|
// hand). That path must not attempt the regen at all: skip here and let a
|
|
37
37
|
// render-capable ship (the laptop path, which CAN render the PNGs) refresh them
|
|
38
|
-
// as a matched .mmd+.png+assertions set
|
|
39
|
-
// staleness in the meantime. Skipping entirely (vs --no-render) avoids committing
|
|
38
|
+
// as a matched .mmd+.png+assertions set. Skipping entirely (vs --no-render) avoids committing
|
|
40
39
|
// a .mmd/.png mismatch to main. Mirrors gen-diagrams.js's own non-fatal skip when
|
|
41
40
|
// bash itself is absent (ENOENT).
|
|
42
41
|
if (pushVia() === 'server') {
|