Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/devteam/dashboard/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2139,6 +2139,8 @@ <h4><a class="title-link" href="#goal/${encodeURIComponent(g.id)}">${esc(g.title
// A routine act (a merge the gates passed, a worker started, a default applied) is its own call under
// the constitution; it is in the Act tab, and here only on request, so judgment calls stay in view.
const routine = (r) => r.kind === 'act' && r.under?.authority === 'constitution';
// A decision that is complete in itself: a noise ruling explains a signal, and no act carries it out.
const NO_ACT_CLASSES = ['orient-noise'];
function renderDecisions(o) {
const all = (o.decisions || []).filter((r) => $('dec-mechanical').checked || !routine(r));
const sel = $('dec-class');
Expand All @@ -2159,7 +2161,7 @@ <h4><a class="title-link" href="#goal/${encodeURIComponent(g.id)}">${esc(g.title
<div class="card small ${r.correction ? 'corrected' : ''}" data-dec="${esc(key(r))}"${subj(decSubject(r), r.id, r.what)}>
<div><div class="act-head"><span class="badge ${r.kind === 'correction' ? 'high' : r.who === 'owner' ? 'good' : 'medium'}">${esc(DEC_KIND[r.kind] || r.kind)}</span> ${linkify(r.what)}</div>
${r.rationale ? `<div class="act-detail">${linkify(r.rationale)}${r.options?.length > 1 ? ` <span class="muted">(weighed: ${r.options.map(esc).join(' / ')})</span>` : ''}${r.confidence != null ? ` <span class="muted">· ${Math.round(r.confidence * 100)}% sure</span>` : ''}</div>` : ''}
<div class="meta"><a class="linkish" href="${open}">${esc(when(r.at))}</a> · ${esc(r.who === 'owner' ? 'you' : who(r.who))}${r.class ? ` · ${esc(words(r.class))}` : ''}${under ? ` · ${under}` : ''}${evidence ? ` · ${evidence}` : ''}${r.kind === 'decision' && r.acts.length ? ` · carried out by ${r.acts.length} act${r.acts.length > 1 ? 's' : ''}` : r.kind === 'decision' ? ' · not acted on yet' : ''}${r.reversible === false ? ' · can’t be undone' : ''}</div>
<div class="meta"><a class="linkish" href="${open}">${esc(when(r.at))}</a> · ${esc(r.who === 'owner' ? 'you' : who(r.who))}${r.class ? ` · ${esc(words(r.class))}` : ''}${under ? ` · ${under}` : ''}${evidence ? ` · ${evidence}` : ''}${r.kind === 'decision' && r.acts.length ? ` · carried out by ${r.acts.length} act${r.acts.length > 1 ? 's' : ''}` : r.kind === 'decision' && !NO_ACT_CLASSES.includes(r.class) ? ' · not acted on yet' : ''}${r.reversible === false ? ' · can’t be undone' : ''}</div>
${r.correction ? `<div class="err">Corrected by ${esc(who(r.correction.by))}: ${esc(r.correction.reason)}${r.correction.repaired ? ` · ${esc(r.correction.repaired)}` : ''}</div>` : ''}
${mine ? `<div class="row-inline" hidden data-correct-form="dec:${esc(r.id)}"><input type="text" data-reason="dec:${esc(r.id)}" placeholder="Reason"><button class="ghost" data-correct-send="dec:${esc(r.id)}">Send</button><span class="err" id="e-dec:${esc(r.id)}"></span></div>` : ''}
</div>
Expand Down
4 changes: 2 additions & 2 deletions docs/devteam/governance/constitution.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Some owner decisions can be handed to the supervisor a step at a time, as eviden
"incident-fix": { "ceiling": 3, "start": 2, "about": "open and run a ticket that fixes a production incident the sensors found" },
"production-remediation": { "ceiling": 2, "start": 1, "about": "restart, scale or roll back production with a reviewed remediation script" }
},
"constitutional": ["merge-word", "approve-feature-plan", "merge-chore", "worker-slots", "replan-within-feature", "add-ticket", "approve-spec", "approve-plan", "approve-design-change", "contract-ruling", "improvement-trial", "release-staging", "goal-readiness", "apply-default", "withdraw-question", "recovery", "carry-answer", "drain", "undrain", "goal-hold", "fleet-drain", "fleet-park", "fleet-undrain", "fleet-unpark", "registry-edit", "goal-impact"],
"constitutional": ["merge-word", "approve-feature-plan", "merge-chore", "worker-slots", "replan-within-feature", "add-ticket", "approve-spec", "approve-plan", "approve-design-change", "contract-ruling", "improvement-trial", "release-staging", "goal-readiness", "apply-default", "withdraw-question", "recovery", "carry-answer", "drain", "undrain", "goal-hold", "fleet-drain", "fleet-park", "fleet-undrain", "fleet-unpark", "registry-edit", "goal-impact", "orient-noise"],
"owner_only": ["one-way-door", "check-override", "governance-edit", "process-edit", "gate-change", "goal-change", "strategy-change", "goal-met", "registry-attest", "new-dependency", "artifact-publish"],
"owner_rules": ["gate-change"],
"reasons": [
Expand All @@ -66,7 +66,7 @@ Some owner decisions can be handed to the supervisor a step at a time, as eviden
}
```

- **Three kinds of decision.** `classes` are the owner's decisions that can be delegated, each with a start rung and a ceiling. `constitutional` are the supervisor's own; they are logged as decisions or acts and a correction of one feeds knowledge, never authority. `owner_only` stay at "ask" whatever the evidence; of those, the classes in `owner_rules` (gate-change) may also be settled by a standing rule the owner gave themselves (`answer … --rule … --rule-scope class:<class>` from the dashboard, their terminal or chat), which then approves that class's calls and merges as an answer would. A project may lower a ceiling in its settings (`autonomy_ceilings`), never raise one; `validate` fails if it does. `registry-edit` (a url link added, changed or removed) is constitutional: the owner corrects it like any act, and a correction becomes a lesson scoped `link:<id>`, not a trust drop. If the owner wants trust semantics here later, it becomes a ladder class at start 3, ceiling 3. `goal-impact` (how much a goal matters, judged against the strategy) is constitutional too: the owner's correction sets the goal's impact to the value they give and becomes a lesson scoped `class:goal-impact`, not a trust drop. `registry-attest`, the owner's yes that the registry lists everything the project depends on (`planning.md` §7), is owner_only: never defaulted. `artifact-publish`, the owner making a landed artifact readable by anyone from the cloud box or taking it down, is owner_only and the one class of act the owner writes (`artifact publish|unpublish`, which the gateway's confirm page runs): a record of what the owner did, never a call, so trust, decisions and corrections leave it out, and the log refuses it from anyone else. A preview (a running dev server the owner can open) is run state, approves nothing, and is never a record.
- **Three kinds of decision.** `classes` are the owner's decisions that can be delegated, each with a start rung and a ceiling. `constitutional` are the supervisor's own; they are logged as decisions or acts and a correction of one feeds knowledge, never authority. `owner_only` stay at "ask" whatever the evidence; of those, the classes in `owner_rules` (gate-change) may also be settled by a standing rule the owner gave themselves (`answer … --rule … --rule-scope class:<class>` from the dashboard, their terminal or chat), which then approves that class's calls and merges as an answer would. A project may lower a ceiling in its settings (`autonomy_ceilings`), never raise one; `validate` fails if it does. `registry-edit` (a url link added, changed or removed) is constitutional: the owner corrects it like any act, and a correction becomes a lesson scoped `link:<id>`, not a trust drop. If the owner wants trust semantics here later, it becomes a ladder class at start 3, ceiling 3. `goal-impact` (how much a goal matters, judged against the strategy) is constitutional too: the owner's correction sets the goal's impact to the value they give and becomes a lesson scoped `class:goal-impact`, not a trust drop. `orient-noise` (a recurring signal ruled noise, citing the observations it explains) is constitutional too: the owner corrects it like any call, a correction brings the signal back to Orient, and it moves no rung. `registry-attest`, the owner's yes that the registry lists everything the project depends on (`planning.md` §7), is owner_only: never defaulted. `artifact-publish`, the owner making a landed artifact readable by anyone from the cloud box or taking it down, is owner_only and the one class of act the owner writes (`artifact publish|unpublish`, which the gateway's confirm page runs): a record of what the owner did, never a call, so trust, decisions and corrections leave it out, and the log refuses it from anyone else. A preview (a running dev server the owner can open) is run state, approves nothing, and is never a record.
- **Why a question was asked.** Every question names its class and carries `why_asked`: the class's rung and ceiling, and reasons from the table. The dashboard shows it first.
- **Moving up.** Only through the owner. When answering, the owner can say how the next decision of that class is handled (ask, ask with a default, act and report), for the whole class or one scope; it applies at once, up to the ceiling. Or the supervisor asks a `trust` question when, of the last ten answered questions in the class, the owner went with its recommendation nine or more times (an answer equal to the recommendation; defaulted answers don't count); `ask --kind trust` refuses without that evidence, and the question carries those question ids and the uncorrected calls since the class last dropped. At most one open per class, two in all, asked at a retro. The supervisor never moves itself up.
- **Decisions and acts.** A judgment call is a `decision` line (`devteam.mjs decide`): what was decided, the options weighed, why, and the authority it rested on, snapshotted when it was made (the class's rung and ceiling, and the answer, default or standing rule it cites). Acts that carry it out cite it (`act --decision <d-id>`), take its class and stay within its refs. A decision made on the class's own rung lapses if the class drops after it. A mechanical act (a merge the gates passed, a worker started for a ready ticket) needs no decision line and is its own decision; so is every act logged before decisions existed.
Expand Down
7 changes: 5 additions & 2 deletions docs/devteam/process/supervisor.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,15 @@ Two loops run through you. **Goal → design → roadmap → graph → tickets:*
Run it about every 10 minutes when PRs are converging, 30 when quiet. Between ticks, wait with `devteam.mjs wait [--minutes n]` (in the background where your harness can be woken by a finished command): it returns when a worker messages you, the owner does anything (an answer, a goal decision, an ack), a new high finding appears, or the tick is due (`tick_minutes`, default 15), and says which. Don't write your own watch loop: one that wakes only on messages and answers misses a worker that stopped silently on an API error, since a stopped worker sends nothing. A high finding already open when `wait` starts returns at once: act on it, or `ack` it with a reason. Each tick is one pass of the loop:

**Observe**

A reading (a sensor's daily rollup, a goal reading, a `spend` line, a `gate` run) is a measurement: it shows on charts and in Health, and is never a signal Orient groups. An observation is a reading escalated because there is cause for action, or something noticed; where it came from (`provenance`: one of the schema's `x-ooda.sources`, such as `sensor`, `metrics`, `owner-answer`, `worker`) and its stage in the loop are computed when it is read, so read `provenance.from`, not `source`, which keeps whatever its writer gave. When what you record is the owner's words (something they said in your session or a thread that isn't itself an answer), `observe --source owner-chat --ref <id>` (or `owner-answer`, `owner-thread`), naming the record that holds them: the question they answered, or their turn or thread. Without such a record it is your observation, and reads as yours.

1. `git fetch origin` (home's), then step 2's `devteam.mjs observe-metrics`, which fetches each repo link's main (four at a time, inside the tick's 20 s network budget, before its probes) so everything after reads every repo as its origin has it now; then `devteam.mjs status --json`; note new merges. `devteam.mjs next` prints the owner's next best step: the top of the Inbox, and why it is there. A sibling whose main it can't fetch is a `repo-unreachable` finding naming the link; its main stays as last fetched until a later tick's fetch succeeds.
2. `devteam.mjs observe-metrics`: first it fetches each repo link's main (step 1), then it records what every agent spent since the last tick as `spend` lines (from the harnesses' session logs, which get cleaned up; the log keeps it), then each new finding (flaky gate, slow test, blocked ticket, stale worker, stalled inbox, idle capacity, cost hotspot…) becomes an observation, once a day per finding. A finding you have looked at and decided can wait: `ack <kind> <subject> --reason "…"` sets it aside for `finding_ack_hours` (it comes back sooner if its subject changes, and the dashboard lists it with your reason); fix the cause instead when there is one (a held ticket, a lowered cap). Record anything else you notice about the graph, the code or the project as an observation too (`observe --subject … --kind … --detail … --severity 0-4 --valence -2..2 --key …`): estimates that missed, tickets added after a plan landed, a design change planning should have caught. `observe-metrics` also probes the registry once a day (`link-unreachable`; a page that answers 401, 403 or 405 is the low `link-behind-auth`), checks main's lines (`unowned-files`: no new ticket starts until a chore extends a line; an owns glob that matches nothing is the low `line-empty`, which blocks nothing), watches for `line-pressure`, and raises `merged-out-of-order` for a ticket that reached its main before a ticket it depends on (`merge` refuses that, so it was merged by hand or in the forge's UI): check the ticket works without it and merge the dependency next.

**Orient**
3. Fix drift (§4).
4. `devteam.mjs orient`: every recurring signal (three or more observations or corrections on one key) gets explained: a lesson (`knowledge add --kind lesson --scope … --evidence …`, with `--kit` when it is about the kit rather than this project, §9 Share upstream), a problem (`problem add`), or noise (say so in an observation). A candidate lesson waits in the owner's inbox to keep or retire; accept one yourself (`knowledge set <id> --status accepted`) only when the evidence is plain, and the owner can still retire it. Accepted knowledge goes into the next prompts in that scope. Triage open problems: assign, mitigate, resolve. Read the owner's recent resolutions (`log --type observation`, kind `thread-resolved`): what they keep raising, about which goals and pages, and what they resolved without a change. Three threads on one goal is a signal about the goal: orient on it. For each new problem or incident, and each observation of severity 3 or more, `search "<its words>" --limit 5`: it may have happened before under other ids.
4. `devteam.mjs orient`: every recurring signal (three or more observations or corrections on one key) gets explained: a lesson (`knowledge add --kind lesson --scope … --evidence …`, with `--kit` when it is about the kit rather than this project, §9 Share upstream), a problem (`problem add`), or noise: `decide --class orient-noise --refs <signal key> <observation ids> -- "<why>"` (it clears the signal; the owner can correct it, and a corrected ruling brings the signal back). A sensor's qualifies and spikes on one fingerprint are one signal, keyed by the fingerprint's id. A ruling that names the signal's key covers all of its observations up to the ruling, so it needn't list them; one after it gets a fresh look (use `sensor ignore` for a fingerprint that is always noise). A fingerprint an open incident holds is in hand, not a signal. A candidate lesson waits in the owner's inbox to keep or retire; accept one yourself (`knowledge set <id> --status accepted`) only when the evidence is plain, and the owner can still retire it. Accepted knowledge goes into the next prompts in that scope. Triage open problems: assign, mitigate, resolve. Read the owner's recent resolutions (`log --type observation`, kind `thread-resolved`): what they keep raising, about which goals and pages, and what they resolved without a change. Three threads on one goal is a signal about the goal: orient on it. For each new problem or incident, and each observation of severity 3 or more, `search "<its words>" --limit 5`: it may have happened before under other ids.
- **Revisit what matters.** You rate; the kit orders. A goal with no impact (`goal-unjudged`), or one whose impact was corrected at a retro: `goal impact <g-id> <1-4> --rationale "…"`, judged against the strategy's "How it wins" order and never against how much work the goal is (`planning.md` §1). Judge again when the strategy changes. An open question whose stake or urgency has changed (work stopped on it, a ticket merged, another question now covers it): `rate <qid> --urgency 3`. A new question that an open one would settle: `rate <gate-qid> --settles <qid>`, which adds it to the list. An unrated question (`question-unrated`): rate it.

**Decide**
Expand Down Expand Up @@ -102,7 +105,7 @@ Production is watched by **sensors**, and they are yours: you add, change and st
- A rollback across a `production-migration` act, or a release whose range touches a migration in git (a release phase runs those with no act; a release the repo can't anchor to a commit fails closed), is a one-way door: ask it `--kind one-way-door`, with no default, and the owner answers it (in the Inbox or in this chat, it's the same).
- Asking, deciding and running follow Upkeep's hold; `--force` overrides it, so use it only with the owner's word.
- A new or changed remediation is a chore ticket, and its settings change (the entry and its `sha`) is `governance-edit`.
- **Findings.** `sensor-down` while it stays down, `cert-expiring`, `slo-breach-week`; `sensor-failing` (3 failed runs; a lapsed login says so), `sensor-stale` (not polled in 3 times its interval while you run), `probe-drift` (it ran clean, then printed lines that don't validate: the provider changed), `probe-costly` (timed out, over the caps, slower than `budget.run_s` or half its timeout, or a `quota…remaining` metric under 10%), `probe-noisy`, `probe-split`, `probe-merged`, and `incident-unowned` (severity 3 or more, open over an hour, no decision).
- **Findings.** `sensor-down` while it stays down, `cert-expiring`, `slo-breach-week`; `sensor-failing` (3 failed runs; a lapsed login says so), `sensor-stale` (not polled in 3 times its interval while you run), `probe-drift` (it ran clean, then printed lines that don't validate: the provider changed), `probe-costly` (timed out, over the caps, slower than `budget.run_s` or half its timeout, or a `quota…remaining` metric under 10%), `probe-noisy`, `probe-split`, `probe-merged`, `sensor-untriaged` (a fingerprint's escalation of severity 3 or more, `sensor_defaults.triage_overdue_hours` (default 2) old while you run, that no incident holds, no ignore covers and no noise ruling cites: fold it, ignore it, or rule it noise), and `incident-unowned` (severity 3 or more, open over an hour, no decision).

## 4. Fixing drift

Expand Down
Loading
Loading