Written 2026-09-24 by the desktop session that shipped PRs #100–#106 with the maintainer
(Carlos Montero, GitHub MoebiusX). It says where the three repos stand, how the maintainer
wants work done, what is on the backlog and which debts we knowingly left. Read it with
docs/BUILD_JOURNEY.md, docs/VALUE_BACKLOG.md and docs/CHANGELOG.md (Unreleased).
The last two days built the second studio journey, Build (Define · Compile · Verify), next to Discover · Diagnose · Remediate. In order:
| PR | What it did |
|---|---|
| #100 | Build number in the footer, the CLI and /api/version (server/build-info.mjs). |
| #101 | The pack library (library/products/*.library.yaml, library/archetypes/*), the engine (tools/lib/library.mjs: tier requirements, scaffold, instantiatePack, placeholders as library.todo.* annotations, provenance), packc init, the /api/library/* routes and the studio journey with the first decision on the landing (Check an existing service or pack / Build a new pack). |
| #102 | Build is a live scan: the layer stack (L1 … L5, GOV) on every step, drawn by the same adapter as Discover. |
| #103 | The pack is the axis: a definition column on the left, the stack as the main surface, a per-layer sheet with the L1 SLI rolodex. |
| #104 | The tier is a seed, not a constraint: any SLI at any tier; library SLIs are editable copies (overrides), custom SLIs from scratch; the definition column becomes a wizard stage (Seed the pack → a read-only seed card with Change seed). |
| #105 | The pop-up SLI editor: one modal over an SLI and its SLO (id, description, metric, objective, window, bound, unit, resolved PromQL), opened from any L1 card on the stack or in the rolodex, live-applied on Define and Compile; id and semconv_metric overrides in the engine. |
| #106 | Spec 1.3 adopted: good_when: below | above on threshold SLIs, vendored under vendor/observability-pack-spec/v1.3/, SPEC_VERSION = '1.3', the burn-rate generator, the boards, the cards and the editor's Bound row know the direction. |
npm test passes on Node 22.16+ (the package.json engines floor, for node:sqlite;
the Node 22.22 pipe truncation in packc journey run --all --json that failed
tools/test-journey.mjs was fixed in STORE_PLAN.md slice 1). npm run lint:
0 errors, 156 warnings on codex/services-api (a baseline of preserve-caught-error-style
warnings; slice 2a removed five with the file readers it deleted, slice 3a some unused catch
bindings, slice 3b added none, slice 4a removed nine with the index code it deleted and 4b
added none; do not add to it). CI on a PR: validate
(includes the vendored-spec check) and backend-live on the
latest 22, node-floor (npm test on exactly 22.16.0), store-prestore (from slice 2b:
the Export gate against a v0.4.0 worktree), windows (node 22.16.0) and windows (node 22)
(npm test on windows-latest, the summary last) and windows-vs-linux (the cross-leg check:
each Windows run's tests, harness assertions and skips against node-floor's). node-floor and
validate end with the summary too: tools/tap-summary.mjs prints the totals and every
failure at the end of the job's log, where the log API's tail reaches them.
refresh-live-pack runs only on demand or when the fetcher changes.
The store (backlog 0) — slices 1 and 2 on develop; slice 3a (roles enforced) and 3b (the identity API) delivered; slice 4a (the pack registry on the store, PR #140) and 4b (services, environments, MCP endpoints) delivered; slice 5 (the audit) delivered; slice 6a (the Services home, the service page) delivered; slice 6b (Settings) delivered; every STORE_PLAN slice is built.
Slice 1 (the store foundation: server/store/*, packc store backup / restore, the k8s
store volume) is PR #109. Slice 2a (PR #111, branch codex/store-identity, stacked on it)
moves identity onto the store: start() runs bootStore() (server/boot.mjs: the boot
order, the one-time import of users.json / orgs.json, the seed and the fail-closed
checks), sessions carry a per-user epoch, OIDC users are recorded under an issuer key,
tenancy is always on, and npm run users / npm run orgs are entry points over
server/identity-admin.mjs. Slice 2b (branch codex/store-offline-ops, stacked on 2a)
adds the offline operations in server/store/ops.mjs: packc store export (the rollback:
to a directory, or in place with the default org's move), import --replace (the request;
the replace itself is boot step 3 in server/store/import.mjs), rekey-issuer --to /
--clear, purge-org, and restore's warning when the marker names another store. The
boot's step-2 refusals now name import --replace (edited files) and both rekey-issuer
commands (a changed issuer); server/test-store-boot.mjs and server/test-store-import.mjs
pin those texts. The Export gate runs in npm test through
server/fixtures/pre-store-build.mjs, and against the real v0.4.0 build in the CI job
store-prestore (tools/test-store-prestore-live.mjs). README "Upgrade And Roll Back" and
deploy/k8s/README.md state the upgrade and the clean rollback (export in place first,
with the server stopped). develop is promoted to main only once 2b has merged.
Slice 3a delivered (PR #119, branch codex/roles-enforced): roles enforced, the live pack per org.
Every route registers authorize('<METHOD> <path>') (server/authz.mjs) as its first
handler and is classified in server/route-table.mjs; the guard applies it (a viewer reads,
an operator writes, an owner is an admin everywhere, the bearer an operator; every refusal
carries denied). server/test-authz.mjs holds completeness, the independent
EXPECTED_CLASS, a probe per /api route and the AuthZ matrix over every posture. The
live pack is per org (<org root>/live/production-live.pack.yaml) and the MCP URL keeps no
credential parameter (tools/lib/mcp-url-safety.mjs); the maintainer's decisions for it:
members are listed to admins only, viewer affordances in the studio landed in 6a (a control a
viewer cannot use is drawn disabled with the server's reason), drafts keep no credential
parameter either (with a warning), and the old install-wide live pack is not moved.
Slice 3b delivered (branch codex/identity-api, stacked on 3a; PR pending): the identity
API. server/routes/identity.mjs registers the owner routes (/api/admin/*: users —
create, disable, enable, a temporary password, sign out everywhere, the owner flag — orgs
and the join role) and the admin routes (/api/org*: the request's org's name and
members; no path names an org); server/auth.mjs adds the self route
POST /auth/signout-others ("sign out my other sessions" in the account menu; the
re-issued cookie keeps its expiry). Every rule is server/identity-admin.mjs's, called with
surface: 'api' — only the way out a refusal names differs from the CLI's, and
server/test-store.mjs pins both columns; a rule's refusal is 400 / 404 / 409 by its kind,
never 403. Every identity change needs X-Observogram-CSRF: 1 in every posture (the bearer,
an operator, never reaches one); without sign-in
the identity API answers only a direct loopback request and is closed beyond loopback.
server/test-identity-api.mjs holds every route's exact answers and audit rows; the AuthZ
matrix in server/test-authz.mjs covers the new routes, and its README tests pin the API
Surface and The Identity API tables to the route table. The maintainer's decisions for it:
admins list members, enable and owner grant / revoke ship (the revoke leaves the default
org's admin membership, and says so), the join role admin needs "confirm": true, and
the open-loopback posture keeps the HTTP identity API behind the direct-loopback rule.
Slice 4a delivered (branch codex/pack-registry, from develop after 3b; PR #140): the
pack registry on the store. packs/index.json is read once, at boot step 5
(server/store/pack-import.mjs), and frozen in place; a pack's label, source, times and the
services it names are rows (server/pack-registry.mjs over server/store/packs.mjs and
pack-links.mjs; tools/lib/service-keys.mjs is the one copy of the service rules, read by
the server and the studio). Services and environments are backfilled from the registered
packs with no tier; every register, adoption, prune, dedup, eviction and RESET is an audit
row, and RESET keeps the services. The index hashes live under
schema_meta.pack_index_hashes in a canonical form without lastUsedAt — never in
legacy_hashes or the marker — so a rollback to 0.5.0 restarts freely and a rollback that
only read packs re-upgrades with one bookkeeping line, while one that registered, relabelled
or removed a pack refuses naming packc store import --replace, which re-imports the file.
packc store export in place reconciles the rows with the pack files, then writes
index.json per org root; the Export gate asserts the labels as v0.4.0 reads them
(server/fixtures/pre-store-build.mjs catalogWithLabels) and
server/fixtures/store-050-guard.mjs holds v0.5.0's guard loop verbatim (the proof the key
separation matters). The design's §19 decisions were all taken as recommended (null tiers,
the plan's guard rule, the aggregate regex verbatim, RESET keeps rows, the canonical
comparison with the export reconciling first). Recommended, not built: a CI job over a
v0.5.0 worktree running the live suite against that build (it needs OBSERVOGRAM_DB per
start, so the suite's child env would have to grow a --build flag).
Slice 4b delivered (branch codex/services-api, from develop after 4a; PR pending):
services, environments and MCP endpoints over HTTP. Every rule lives once in
server/service-admin.mjs (a sibling of identity-admin.mjs, the same AdminRefusal kinds),
the fourteen routes in server/routes/services.mjs (the handler helpers routes/identity.mjs
had — pathId, refused, bodyOf, handler — moved to routes/util.mjs, texts unchanged).
Operators manage services (slug fixed; tier null = graded by the pack) and environments
(bindings, endpoints, an optional MCP endpoint); admins manage the org's MCP endpoint
records, whose three changes take the identity API's defences — the route table's direct /
closedAs fields parametrise authz.mjs's step texts and csrfAlwaysText, byte-identical for
identity entries. mcp_endpoints.url refuses credential-looking parameter names by 3a's
word rule; readTokenEnv must be OBSERVOGRAM_ORG_<ORG>_<NAME> and the request's org's — the
owner is the org with the longest matching prefix, live or removed orgs included, re-checked
at request time by resolveMcpTarget. GET /api/packs/:id/conformance grades at the record's
tier (declaredTier is the graded tier; tier.pack, tier.mismatch; tools/lib/conformance.mjs
untouched). refresh-live, draft-from-mcp, deploy, deploy-bulk and rollback take
mcpEndpointId; the read token comes from the variable at request time, write tokens stay per
request. Suites: server/test-service-admin.mjs, server/test-services-api.mjs; test-authz's
synthetic entries, test-tenancy's ORG_SCOPED, test-smoke's restricted-tier fake reused for
the token case (no new tool literal). The design's §19 decisions were all taken as
recommended (OBSERVOGRAM_ORG_<ORGKEY>_* with the longest-prefix owner, the MCP endpoint
mutations behind the identity API's defences, null tiers, the tier enum, environments.endpoints
in full to viewers). The studio's pickers list the org's endpoints first and keep a typed URL
for an admin only — list-only for every reader the server refuses a typed URL (rebadge batch 3,
C0); a chosen endpoint is sent as mcpEndpointId (6b). The deploy audit (deploys.jsonl → rows)
was slice 5.
Slice 5 delivered (codex/audit): the audit. Every change a principal makes through the
server is a row, and the rows can be read. The file-first routes — deploy, deploy-bulk,
rollback, verify, the journey capture and run, the live refresh — write their row after their
file through server/audit-after.mjs (auditAfter, actorForRecord, bounded, finite): on
its own, never inside the operation; a failed insert never fails the operation (auditError on
the response, one stderr line) and a deploys.jsonl line that could not be appended still gets
its row, flagged detail.fileError: true. The rows carry counts and the MCP origin, never a URL
with a path, an error message or a tool name; journey.run is written for every attempt past
the 404 (outcome error | vantage-lost on a 502). The actor in deploys.jsonl is the
principal's (req.observogramPrincipal.actor through actorForRecord(), checked before the
line is appended; the login, the bearer's label, local), so viewers of GET /api/deploys see logins — the CHANGELOG names both changes.
GET /api/audit (server/routes/audit.mjs; the rule once in server/audit-admin.mjs —
parseAuditQuery(query, { owner, org }) → { scope, org, filters, limit }, the WAYS texts,
auditView) reads the org's rows to its admins (scope=org only) and the deployment's to
owners (scope=all default, org, deployment), filtered (actor, action, kind,
targetKind, targetId, since / until), paged by next / before with a cap of 500;
the row is admin, exposed: 'refuse', direct: true, closedAs: 'the audit API' (the
texts parametrised in 4b say "the audit API"). server/store/audit.mjs gained the kind
prefix predicate and DETAIL_MAX = 8192; the route table lost later. The design's §19
decisions were all taken as recommended (a CLI reader as the last, droppable commit; the
journey.run row on a 502; the fileError row; no email in any row — STORE_PLAN §5's one
changed sentence; viewers see logins; an org admin sees a non-member owner's actor as is).
Suites: server/test-audit-api.mjs (new); test-store, test-deploy-helpers, test-smoke,
test-tenancy, test-authz, test-auth-oidc, test-chain-history, test-store-guards extended.
packc store audit (tools/store-admin.mjs, design §8) lists the audit from a shell —
one JSON row per line, the route's filters as flags, the same rule through parseAuditQuery's
cli surface, no row written. Settings → Audit reads it, paging by next (6b).
Slice 6a delivered the Services home and the service page on the table
(codex/services-axis). Slice 6b delivered Settings (codex/settings, then
codex/settings-owner): environments, the org's MCP endpoint records and the pickers on
mcpEndpointId, members and the org's name, the audit; for owners, users (a temporary
password shown once and changed at first sign-in), organisations (an owner acts in one they
are not a member of) and the join role. Every STORE_PLAN slice is built.
Spec 1.3: good_when with default: below, RFC-0002 accepted 2026-09-23, Go type
SLI.GoodWhen + EffectiveGoodWhen(), seven linter tests, a floor SLI in the example.
Upstream main still serves 1.2 until the maintainer promotes develop; the studio's
"spec v1.3" links therefore point at the vendored commit, not at main.
Untouched by this chain. It vendors Observogram's generators under vendor/observogram/
and must re-vendor for 1.3 (see backlog D). It is a live lab on the maintainer's
machine (Docker Desktop, IBM MQ, 20+ containers); a cloud session should limit itself to
its static parts (site templates, docs, check-rules) and never run the harness.
These are the rules we learned the hard way; treat them as standing instructions.
- Proof, not claims. Every feature is driven live before it is reported (a real browser or the API), with exact labels and numbers. "Tests pass" is not evidence that the UI works. When he says "where can I test it?", the answer is a URL or a branch to pull, never a PR link alone.
- The tier is a seed, not a cage.
minTierdecides what the seed pre-selects and which rubric grades; it never forbids an SLI. Everything the library copied in is the user's to edit (copy-on-write over the library default, per-field reset); an edited PromQL drops the library's evidence claim honestly. - Editing is a pop-up, not a form that scrolls inside a card. Five fields fit in one dialog; live apply with a status line that names what changed.
- Apple-like restraint in the studio: generous spacing, 14–16 px radii, real switches
and segmented controls, one accent per layer, 200 ms motion under
prefers-reduced-motion, WCAG AA in both themes (a test scans the stylesheet). - Honesty rules (from the run records and the Build journey): "pass on a placeholder" is never plain green; a gap is a gap, never a zero; a section switched off says which clauses fail with it (read from the engine, never predicted); stack samples are signals, not verdicts.
- The spec is his standard. A change that needs a new field goes through the spec repo
first (schema + spec text + RFC + Go linter + example), then Observogram vendors it with
checksums (
node tools/sync-spec.mjs --ref <commit>/--check), then the MQ lab re-vendors the generators. - Branching (
docs/BRANCHING.md):codex/<topic>→ PR againstdevelop;mainadvances only by promotion PR. Single-concern commits, each green alone, bodies that say why. He merges within minutes: check a PR's state before pushing follow-ups, and open a new PR for work that lands after a merge rather than pushing to the merged branch. - Studio conventions (
docs/UI_CONVENTIONS.md): views importstudio/host.mjs, neverapp.mjs; loaders, pure models and renderers are separate exports; models read no state and fetch nothing and are tested undernode:test; a journey's actions ridehost.<namespace>(host.build,host.services,host.settings), built by the controller and handed to the renderer; the studio's persisted state is one login's in one org (studioState.v2:<login>:<org>); the AA scan readsapp.csszone by zone (a.set-*rule under the==== Settingsmarker) and a zone's block comment records whatux.css/reskin.cssoverride; a secret is shown once and never stored (a temporary password in its dialog only; an MCP read token is a server variable's name); the MCP pickers sendmcpEndpointId;tools/libstays free ofnode:*(it is served to the browser at/lib, imported at call time, never statically);tools/test-studio-graph.mjslinks the module graph onnpm test. - Reviews before he sees it. The pattern that worked: implementer → two parallel reviewers (one drives the thing as a user and tries to break it; one checks conventions, accessibility, tests, per-commit hygiene, docs vs code) → a fixer that reproduces each finding before changing anything, one commit per finding with the test that would have caught it. Findings that were not reproduced are not fixed.
npm test # node:test suites (Node 22.16+), the studio graph, the AA scan
npm run lint # eslint: 0 errors is the bar, 166 warnings the baseline
node tools/sync-spec.mjs --check # vendored spec files match VERSIONS.json
PORT=8013 OBSERVOGRAM_AUTH=off OBSERVOGRAM_WORKSPACE=/tmp/ws node server/index.mjsOBSERVOGRAM_AUTH=off removes the sign-in; the workspace must be a throwaway directory.
The server binds 127.0.0.1. For pictures and real-input drives use headless Chromium over
CDP from a Node script (--headless=new --remote-debugging-port=93xx --user-data-dir=<tmp> --window-size=1920,1080, then Page.navigate, Runtime.evaluate, Page.captureScreenshot
with captureBeyondViewport); in the cloud sandbox Chromium lives at
/opt/pw-browsers/chromium. Useful API truth checks: POST /api/library/instantiate
(entries, name, tier, environment, owners, params, toggles, overrides, custom) returns
canonical, todos, provenance, warnings, schemaErrors, summary, conformance, adapted;
POST /api/library/compile takes the same inputs plus target.
0. Make it feel like a product — first. The maintainer's steer of 2026-09-24, verbatim:
"making it feel like a product is the most important. Maybe it's time to deploy our own
in-mem SQL DB for user, services and environment management." This supersedes the
"file-first, not a database + user accounts" constraint written into
docs/VALUE_BACKLOG.md item 10 and moves items 10 and 12 (workspace persistence · auth ·
audit · rollback; identity · tenancy · hosted posture) to the front. What existed then:
server/auth.mjs keeps users in <workspace>/users.json (seeded admin / admin on first
boot, OIDC optional), server/tenancy.mjs scopes a workspace per org, packs live as
<workspace>/packs/<id>.pack.yaml, and "service" and "environment" are not first-class
records at all — a service is a pack registration plus the landing's service picker, an
environment is a pack binding or a gen-site partition. The shape to build, keeping the
repo's zero-new-dependency rule: an embedded SQL store on Node's built-in node:sqlite
(DatabaseSync; present in the Node 22.16 the studio runs on, behind an experimental
warning — the Node floor is now pinned in package.json engines and the README), file-backed
under the workspace for persistence with :memory: for tests and demos, one schema module
with versioned migrations, tables for users (with roles), orgs, services (name, owners,
criticality tier, the pack it carries, its environments), environments (name, bindings,
endpoints, the MCP it is checked through), pack registrations, and the audit log; the
existing requireAuth / observogramActor (since slice 5 req.observogramPrincipal) / workspaceRoot() seams stay the integration
points so OIDC and the file workspace keep working during the migration. The studio side is
what he means by "feel like a product": sign in, land in your org, see your services and
their environments as the axis (the landing's Check an existing service picks from this
table; Build's seed writes into it), a settings surface for users and environments, and
Discover · Diagnose · Remediate · Build hanging off a service record rather than a pack
file. Design it against docs/VALUE_BACKLOG.md items 10 and 12 and
docs/RELEASE_READINESS.md, and put the plan in front of him before the first commit —
he ratifies plans for this stream (item 12 says so). Planned 2026-09-24:
STORE_PLAN.md — schema, import, roles, slices and gates; the seven
decisions are ratified; its §9b lists the refinements made since, which merging it confirms.
Status: slice 1 (the foundation) is PR #109; slice 2 is complete once 2a (identity on the
store, PR #111) and 2b (export, import --replace, rekey-issuer, purge-org) merge; slice 3a
(roles enforced, the live pack per org; PR #119) and 3b (the identity API) are delivered; slice
4a (the pack registry on the store, PR #140) and 4b (services, environments, MCP endpoints) are
delivered; slice 5 (the audit) is delivered; slice 6a (the Services home, the service page) is
delivered; slice 6b (Settings) is delivered; every STORE_PLAN slice is built — see §1.
A. Decide: "the draft becomes the pack". The root cause of every remaining Build gap is
that the draft is a set of inputs re-instantiated from the seed on each change, with
overrides and custom patched on top. Consequences today: one SLO per SLI with a derived
id and no budget-policy or label editing; the SLI type is fixed; L2–L5 can only be
switched and parametrised, never composed (no own dashboard, route, probe, chaos
experiment, scrape job or backend). The proposal on the table: seeding instantiates once
into a real pack document held in the draft; every edit is an edit of that pack against
the schema; the engine's job per change becomes validate + grade at the tier + compile;
"Change seed" becomes "reapply library defaults" per item. The pop-up editor was built so
it does not care which it writes to. The maintainer has not yet said go; it is the next
big slice and should start after his answer, not before.
Two design constraints he gave for it on 2026-09-24:
- Every layer's
+adds an artefact, two faces: a parameterised form (the pop-up idiom) and a paste box for the artefact itself — a pack fragment in YAML on any layer, Grafana dashboard JSON on L3 (stored as a source file the pack references, uid and bindings read from the JSON), a scrape config or a Collector pipeline fragment on L2, an Alertmanager route on L4 — parsed, validated, shown as a card with source "pasted" and no evidence claim, refused with the parser's message otherwise. A raw Prometheus alerting rule has no slot in the pack (L4 is declarative); pasting one needs an additive spec field first. - Backpropagate the chain with virtual artefacts. When a downstream artefact arrives
(a pasted dashboard, a rule, a probe), derive the upstream ones it implies as virtual
nodes — metric → SLI → SLO (and the policy an SLO implies) — so the chain is complete and
gradable at once; when the user later defines one explicitly, the explicit artefact
supersedes the virtual one of the same identity (merge: the virtual's identity and
what was inferred become the defaults of the explicit form; or ignore: the virtual node
simply drops out). The precedent already in the engine:
tools/lib/traceability-graph.mjsaddsMETRIC-VIRTUAL-<name>nodes (virtual: true) for metrics an SLI's PromQL references but no L2 artefact declares, and the comparison lets a real node of the same identity satisfy a virtual one;tools/lib/sli-inference.mjsis the compiler's inverse (recording rule names → SLI/SLO identities) on which such inference keys. Virtual artefacts must stay honest on the stack and in the summary: a clause that passes only on a virtual node reads like "pass on a placeholder", never plain green.
B. Neuron SLO ledger (planned with the fleet view in NEURON_FLEET_PLAN.md, slices F1–F3; proposed 2026-09-25). He wants Pyrra's one thing — every SLO's availability, remaining
budget and burn in one place — inside Advanced → Neuron, paired with the integrity of
each SLO's measurement chain (Neuron already computes blast radius). Prerequisite in the
compiler: window-long error-ratio and budget-remaining recording rules per SLO (today only
<svc>:errorbudget:burn_5m / burn_1h and the threshold error_ratio_5m exist); then a
journey run samples them through the MCP metrics tool the inventory check uses and records
them per run; then the Neuron tile, table and small multiples.
C. The scan, slices 2 and 3 (docs/BUILD_JOURNEY.md "The scan"): per-layer visuals
(L1 SLO gauges, L2 pipeline flow strip, L3 dashboard wireframes from the compiled Grafana
JSON, L4 chain, L5 cards) and the isometric slab stack with the acquisition animation plus
landing miniatures. Own PRs, after his go.
D. MQ lab re-vendor for spec 1.3 (in mq-observability-pack, on the live host): copy
tools/lib/burn-rules.mjs, tools/lib/dashboards/* and good-when.mjs into
vendor/observogram/lib, npm run generate, paste the --pack-snippet, decide whether
queue_depth_headroom becomes a plain depth floor with good_when: above, run
check-rules, promtool, and certify:quick against the live stack. Not for a cloud session.
E. Library entries and good_when. No shipped entry declares a direction yet; every
bound is a ceiling and the generator's "looks like a floor — declare good_when" hint still
fires on ratio-unit ceilings (e.g. ibm-mq's headroom). Rewrite the entries that inverted a
floor, with catalogue evidence for any new metric.
F. Spec repo follow-ups: the shipped example fails its own tier-1 rubric on seven
pre-existing clauses (3.4, 3.5, 3.6, 2.4, 1.1, 1.4, 1.9) plus six unknown-alert warnings;
maturity clause 2.1 counts a "latency SLI" by type, so a floor SLI satisfies it (documented,
rubric unchanged — his call); promote develop → main so upstream main serves 1.3.
G. Smaller studio items: the seed card's product chips only open the L1 sheet; the DEFINE silhouette's clickable cards are candidate ghosts, not artefacts; VERIFY's dialog is read-only by design; the SLI rolodex shows the bare library id until a rename.
- A browser twin of a tools/lib helper.
studio/sli-direction.mjsduplicatestools/lib/good-when.mjsbecause the studio cannot importtools/libstatically (unserved relative path;/lib/unresolvable undernode:test). A test holds the two equal input for input. The studio's own pattern for this is a call-timeimport('/lib/…')(Neuron does it); the twin could go that way. tools/lib/validator.mjsskips boolean sub-schemas ("x": false). The 1.3 schema therefore uses{ "not": {} }to forbidgood_whenon ratio/custom SLIs. Fixing the validator would let upstream use the idiomatic form.POST /api/library/compileon library packs warns "declared … and generated … write the same metric name": the library declares theref:slis.*recording rules the compiler also generates. Pre-existing; decide which side owns them.packc inithas no custom SLIs and--overridetakes only the scalar fields andid/semconv_metric/good_when; PromQL edits are studio/API only. Documented inlibrary/README.md.- Records are most of the way onto the store. Users, orgs, memberships, the owner and
the audit live in the store from slice 2a;
users.json/orgs.jsonare imported once and never read again. From slice 4a the pack registry is rows too (packsandpack_services;packs/index.jsonis read once, then frozen and compared), and services and environments are records backfilled from the packs; from 4b their API, owners, tiers and the org's MCP endpoints (server/service-admin.mjs,server/routes/services.mjs). Artefacts (packs/*.pack.yaml, snapshots, journeys, runs,deploys.jsonl) stay files by design. The rollback to a pre-store build or to 0.5.0 ispackc store exportin place, with the server stopped (it writesindex.jsonfrom the rows), and the way back after one that changed users, orgs or packs ispackc store import --replace(slices 2b and 4a). - The Build state is inputs + overrides (see backlog A). Until that changes, every new editable thing needs its own special case in the engine and the state.
- Distribution SLIs get no burn rules (null legs) and no direction handling beyond the schema; nothing in the studio creates one.
- Golden churn.
tools/fixtures/golden/compile/*andreference-packs/dashboards/*.jsoncarry the spec version in a banner line and the direction glyph in descriptions; every spec bump regenerates them. The drift-scan test intools/test-validator.mjscarries an explicit allow-list of historical documents. - The repo-root
index.html(the v0.3-era landing page) still says "Spec v1.2" and links upstreammain; it is not scanned by the drift guard. - Lint baseline: 166 warnings, mostly
preserve-caught-error; each new file should add none. - The MQ lab's
sites/lab/(the renderingnpm run sitewrites) is untracked and unignored in that repo; it is a copy ofstack/plus site-only files, not something to commit. A.gitignoreline is the fix. - Windows-only traps the desktop session hit (a junction for
node_modulesthatrm -rffollows;MAX_PATHbreakinggit worktree addunder long scratch paths;go test -raceneeding CGO) do not apply in the cloud sandbox, but a local session on the maintainer's machine will meet them again.
docs/BUILD_JOURNEY.md— the Build contract end to end: the library format, the seed and the copies, the axis, the sheet, the editor, the API and the CLI.library/README.md— the entry format,instantiatePackinputs (overrides,custom,good_when), the caps, the CLI flags.docs/STORE_PLAN.md— backlog 0: the embedded store, its import from today's files, roles and owners, the slices and their gates.docs/VALUE_BACKLOG.md— the product backlog the maintainer curates (P1–P4); items A–C above are the Build-journey additions to it and should be filed there when he ratifies them.docs/CHANGELOG.md(Unreleased) — one entry per PR above, with the review findings each pass closed.docs/UI_CONVENTIONS.md,docs/BRANCHING.md,docs/VENDORING.md,vendor/observability-pack-spec/README.md— the conventions this note assumes.- otel-observability-pack:
docs/rfcs/0002-threshold-direction.md,spec/ObservabilityPack-Spec.md§5.1. - mq-observability-pack:
CLAUDE.md(the rules of the live lab) andSTATUS.md(its own cross-session hand-over).