Skip to content

docs(decisions): add a decision record log alongside design proposals - #56

Merged
myasnikovdaniil merged 9 commits into
mainfrom
docs/decisions-log
Aug 21, 2026
Merged

docs(decisions): add a decision record log alongside design proposals#56
myasnikovdaniil merged 9 commits into
mainfrom
docs/decisions-log

Conversation

@myasnikovdaniil

@myasnikovdaniil myasnikovdaniil commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

This PR adds decisions/ for architectural decisions we already made, next to design proposals that propose them.

Design proposal is intent, written before the work. When implementation contradicts design we revise proposal in place, so it reads as if it always said current thing, and reasoning that changed our minds stays only in PR diff.

It already happens here. #44 and #42 revised DHA to HPA-on-scale-subresource after spike, #53 reopened actuation because cnpg scale subresource has no selector, same shape in #40, #41, #36. Every one is implementation finding rewriting accepted proposal, with why left in PR body. compute-plane proposal even carries a Revision (this PR): paragraph in its metadata because there was no other place to put that rationale.

What is inside:

Root README gets a row in "Where do I file?", Proposal Drift now says to write a record instead of only editing proposal, and proposal template gets Decisions section.

Two rules carry this. Record is never edited after merge, it gets superseded by a new one. Review checks accuracy and not agreement, so one maintainer is enough, decision was already made and if review turns into re-litigating it then it was not settled and belongs in proposal instead.

Please check Deciders line on 0001. I put Andrei Kvapil (@kvaps) and myself. Timofei Larkin review on #17 was half of the objection that drove the change but i did not want to guess handle, so he is named in text only.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 17478251-692d-471a-aa5a-82072ee14cab

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 84052ac4-be87-4632-8364-bad8263a4be2

📥 Commits

Reviewing files that changed from the base of the PR and between 93e347a and efdf056.

📒 Files selected for processing (7)
  • README.md
  • decisions/0001-computeplane-ships-as-an-operator-owned-module.md
  • decisions/README.md
  • decisions/template.md
  • design-proposals/README.md
  • design-proposals/compute-plane/README.md
  • design-proposals/template.md

📝 Walkthrough

Walkthrough

The change adds decision-record documentation, an ADR template, an accepted ComputePlane architecture decision, and guidance for linking decisions to design proposals.

Changes

Decision records and proposal guidance

Layer / File(s) Summary
Decision record workflow
README.md, decisions/README.md, decisions/template.md
Documents decision-record purpose, authoring rules, lifecycle, review expectations, and the ADR template.
ComputePlane architecture decision
decisions/0001-computeplane-ships-as-an-operator-owned-module.md, design-proposals/compute-plane/README.md
Records the operator-owned ComputePlane module decision and links it from the ComputePlane proposal.
Proposal decision guidance
design-proposals/README.md, design-proposals/template.md
Requires significant implementation divergences to link decision records and adds a Decisions section to proposal guidance and templates.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding a decision record log alongside design proposals.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/decisions-log

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@lllamnyp Timofei Larkin (lllamnyp) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: decision records

The diagnosis is right, and the evidence for it is the strongest part of this PR. Design proposals here are intent, written before the work; when implementation contradicts them we edit them in place, so they end up reading as though they always said the current thing. #42 and #44 rewrote the database-horizontal-autoscaling proposal after a spike; #53 reopens its actuation mechanism on a CloudNativePG finding; #40, #41 and #36 are the same shape. The compute-plane proposal carries a Revision (this PR): paragraph in its metadata block because that rationale had nowhere else to live. Something should exist for this, and the writing throughout — the proposal-vs-record table, the "when not to write one" list, the immutability rule — is better than most first drafts of a process.

I want one structural change before this lands, plus a set of corrections. Taking them in the order I think they matter.

1. Records should live with the proposal they amend

Concretely: design-proposals/<name>/decisions/NNNN-slug.md, with the process text folded into design-proposals/README.md and the template at design-proposals/decision-template.md, instead of a root-level decisions/ tree.

The argument that convinces me is the one your own template raises and then waves through. **Proposal:** ... — or none presupposes that a decision can exist here without a proposal. I don't think it can. An architectural decision is a decision with respect to something; this repo's front-door table already routes every candidate — cross-cutting architectural change to a design proposal here, bug or scoped feature to cozystack/cozystack, governance to an issue here. There's no residue. And a deliberation weighty enough to need a record, with no proposal to attach it to, has just demonstrated that it is a proposal.

Once that holds, the rest follows:

  • Both link directions come for free. A reader browsing the proposal directory sees its decisions; a record's parent directory is its proposal. Nothing to maintain, and hand-maintained backlinks are the first thing to rot. The Decisions section added to design-proposals/template.md becomes unnecessary rather than load-bearing.
  • Numbering stops colliding in practice. This repo routinely has five or so PRs open at once, so a global sequence collides constantly, and "whoever merges second renumbers on rebase" breaks any citation already written down. Per-proposal numbering collides only when two PRs touch the same proposal.
  • No new top-level concept. The root README is a routing table; a decisions/ tree adds a second document class to it, and the new "a decision already made" row is slightly false — you never file a decision on its own, you file a proposal amendment that carries one. Per-proposal placement extends the process that exists and adds nothing to the front door.
  • Withdrawn proposals take their decisions with them instead of orphaning entries in a global log.

The case for the root log deserves recording, because parts of it are real:

  • A monotonic id is short, citable and survives a directory rename. True, and the cost of giving it up is real; per-proposal files stay numbered, and citations become compute-plane ADR 0002 — more verbose, more informative.
  • Nygard's convention and its tooling assume one log — adr-tools defaults to a single doc/adr. Real, but nothing in this repo would consume it: there is no CI here at all, and this template already departs from Nygard's four sections.
  • "What did we decide lately?" — answered in any layout by git log -- 'design-proposals/*/decisions/*', better than by directory order.
  • Decisions that span proposals have no single home. This is the one that nearly carried it for me, and the answer is section 4 below.

2. This PR should introduce itself through the mechanism it introduces

As it stands, the reasoning for why decision records exist and where they live will survive only in this PR thread — which is precisely the failure the PR is written to fix. It reproduces its own diagnosis on itself.

So I'd like this to land as:

  • A design proposaldesign-proposals/decision-records/README.md — stating what is being introduced and why, with the drift evidence already in your PR description. Note that a merged proposal is "a reference, not a binding spec", so the operative rules (when a record is required, the template, the immutability rule) still need to land in design-proposals/README.md and the template; the proposal states the change and the rationale, not the law.
  • A decision record under itdesign-proposals/decision-records/decisions/0001-decision-records-live-with-their-proposals.md — recording the placement decision and, in the Why not the alternatives section, what lost: the root-level log, with the four points above stated at their real strength rather than strawmanned.

This isn't ceremony. It gives the format an exemplar written by the person who owns it, from an argument they were present for, rather than leaving 0001 — a backfill of an argument between other people — as the only pattern every future author copies. It also demonstrates per-proposal numbering immediately: two proposals, two records, both numbered 0001.

3. Corrections to the ComputePlane record

You asked me to check the Deciders line. The handle you didn't want to guess is mine, @lllamnyp — but I'd change more than that.

  • The record never cites #33. It links #17, #26, #27 and #39, but not the PR where the decision was actually argued and merged, which is also where its 2026-07-18 date comes from. A record whose job is to be the durable pointer to the reasoning points everywhere except at it. Add a Decided in: field to the template — that would have caught this mechanically.
  • The #17 attribution is wrong. The record says two reviews converged on the objection that ComputePlane should not be a distinct kind, citing #26 and my review on #17. My #17 review was an approval, and its points were: that "hidden from the tenant" was conflated with the security boundary, and the real justification for withholding access is tamper-resistance — withhold admin, not visibility; that the "1-click" framing didn't distinguish from the rejected alternative and placement should target a named cluster; that letting child tenants inherit an ancestor's ComputePlane re-creates the problem ComputePlanes exist to solve; and that there was no story for ComputePlane workloads reaching tenant-namespace services. The "not a distinct kind" argument is #26's alone — #26 quoted my approval summary in support of its own case. What that review did contribute is the tamper-resistance argument, which is the load-bearing rationale of this record's own Decision and Consequences sections, and it's credited to nobody.
  • Deciders should be @kvaps, @lllamnyp — the author of #33 and the maintainer who approved it. myasnikovdaniil's #26 forced the reopening and deserves the credit in Context, but its literal ask (no new kind at all) is not what was adopted, as the record itself says.
  • Implemented in: not yet is false. cozystack/cozystack#3280 merged 2026-07-29.
  • The rejection of the preset-field model rests on the fragile argument. The record justifies the shape through packages/extra/computeplane and the PackageSource convention. The durable argument is structural and survives #39 deleting the extra category: capabilities and schemas are per-kind, so two postures over one chart need two ApplicationDefinitions; ApplicationDefinitionSpec has no fixed-values facility and the conversion makes the tenant's spec become the Helm values, so operator-fixed values have nowhere to live but a chart; and a narrow openAPISchema doesn't substitute, because a field you can default is a field the tenant can override, while a field absent from the schema receives the chart's own unhardened default. "Hardened and unreachable" isn't expressible in a schema.
  • A dropped consequence. #33 was asked to co-land with #39, or land after it, because once tenants set computeplane: true the release-name invariant stops being a markdown disagreement and becomes a migration of live Kamaji clusters holding tenant data. #3280 has since merged and #39 is still open, so that constraint is live now. The record presents #39 as purely composing.
  • Minor: #27 is cited as a revision without noting it was closed unmerged, superseded by #33 the same day. In a document that is explicitly a history, that matters.

4. Process amendments

  • Drop — or none from the Proposal: field. Per section 1, make it required.
  • Add Decided in: for the PR or issue thread where the decision was argued, and one line under Why not the alternatives asking that each rejected option be sourced to the comment or PR it came from. Both of the substantive errors above come from writing alternatives from memory, and accuracy is checkable only if the source is linked.
  • Separate metadata from body in the immutability rule. "The body of an accepted record is not edited" is right, but Status, Superseded by and Implemented in must track reality — the lifecycle depends on it. Say that the header block is maintained and the prose below it is frozen.
  • Record what belongs in code instead. Where a decision's content is a fact about a type, a field or an invariant that code must respect, its primary home is a comment at that site plus a test that fails when it's violated; the record links to it rather than restating it. This project already does this well: the computeplane release-name invariant is enforced at packages/extra/computeplane/templates/check-release-name.yaml and pinned by tests/release_name_test.yaml, whose suite comment carries the mechanism. That guard is a more reliable record than any prose, and it stayed more precise than this record's version of the same invariant. Without this rule the log fills with restatements of things the code already enforces.
  • Enforcement. There is no CI in this repository — .github/ contains only issue templates — so the rule in "Proposal Drift" is only as binding as the place a reviewer looks. Add .github/pull_request_template.md with one checkbox: this PR revises a merged proposal; it adds a record under that proposal's decisions/, or says why none is needed. That is the only enforcement available, and it costs one file.
  • Decide what happens to the existing in-proposal narratives. Compute-plane's Revision (this PR): metadata line and the database-autoscaler's Why this changed section plus spike appendix are three improvised solutions to this same problem, and this PR adds a fourth without retiring any. My preference is extraction, especially for Revision (this PR):, which is unmaintainable by construction — "this PR" stops resolving the moment the next revision lands.
  • The review bar is a governance change. "One maintainer's approval is enough" against the proposal process's "consensus from core maintainers" is probably right for accuracy-checking a settled decision, and the reasoning given for it is sound — but a lighter approval bar for a new document class should be visibly agreed rather than merged as documentation.

5. A second record, from a different subsystem

One backfilled record isn't enough to show the mechanism works, and a log that ships at 0001 and stops is the standard failure. The live candidate is the database-horizontal-autoscaling proposal: rev1's bespoke db-autoscaler operator was rejected after an implementation spike proved its enforcement premise unbuildable on the aggregated apps API. That decision is settled and merged, its rationale currently survives only in a proposal appendix that the immutability rule doesn't protect, and #53's CloudNativePG finding has since reopened the actuation mechanism downstream of it — so it would exercise Revisit if on live content rather than a hypothetical. Either land it here or open the ask on #53; I'd rather not merge the mechanism with one instance.

6. Follow-up: where design principles live

Some of what surfaced around ComputePlane isn't a decision about ComputePlane at all — that ApplicationDefinition has no operator-fixed-values facility, and that settability and defaultability are the same property in a structural schema, are standing constraints on every operator-owned module anyone builds next. A record filed under one proposal is the wrong home for them, and today they're recorded nowhere: ApplicationDefinitionSpec in api/v1alpha1/applicationdefinitions_types.go carries no such comment, and the conversion at pkg/registry/apps/application/rest.go is a bare Values: app.Spec.

Code comments plus tests cover the enforceable half, but there is genuinely no home in Cozystack for a design principle as such — something like an architecture or principles document in cozystack/cozystack. That's out of scope here, but please open an issue for it and link it from the proposal in section 2, so the gap is recorded rather than absorbed into this log by default.


To be explicit about cost: I'm asking for a restructure of a PR that's already complete, and I don't think that's free. But the move is mechanical now — one git mv, one prose fold, four link fixes — and it stops being mechanical once the log has ten entries and citations pointing at it from other repositories. Everything in section 3 is independent of the placement question and needs doing either way.

myasnikovdaniil added a commit that referenced this pull request Aug 21, 2026
The reasoning for why decision records exist and where they live was going
to survive only in the #56 thread — which is precisely the failure the
change is written to fix. It reproduced its own diagnosis on itself.

So it lands as a design proposal, design-proposals/decision-records, with
the drift evidence that was previously only in the pull-request
description, and a record under it for the placement decision. The
proposal states the change and the rationale; the operative rules stay in
design-proposals/README.md, because a merged proposal is a reference and
not a binding spec.

This is not ceremony, on one condition: the record has a losing
alternative. "We will write decision records" has none, and a record for
it would be a form to copy. The placement question has four real arguments
on the other side, and 0001 states them at full strength — the citable
monotonic id, Nygard's tooling, the single directory that lists itself,
and cross-proposal decisions having no home — then says what decided each
one. It also demonstrates per-proposal numbering immediately: two
proposals, two records, both 0001.

Recorded a fifth alternative that the review did not raise and neither did
the original proposal: freezing merged proposals and superseding them, the
Rust RFC and Python PEP model, where the history is automatic because
supersession is the only way to change anything. Rejected — it contradicts
"a reference, not a binding spec" and makes every implementation finding a
new proposal — but it is the model the largest peers picked, and Kubernetes
keeping living KEPs has exactly this gap and fills it with milestone dates
only. A future reader will ask why we did not do that, so the answer is on
the record.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
@myasnikovdaniil

myasnikovdaniil commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

Restructured to per-proposal, section 3 fixed, 4 and 5 done.

  1. Agreed. Records moved under their proposal, process folded into design-proposals/README.md, template at design-proposals/decision-template.md, root README rows dropped.
  2. I thought this was theater and you were right. Backstage ADR001 is literally the decision to keep an ADR log, and govuk design system introduced RFCs and ADRs through proposal 001, so it is how these normally start. Landed as design-proposals/decision-records with its own 0001.
  3. Errors are mine, i backfilled an argument I was not in and wrote the alternatives from memory. Your point that the test comment is more precise than my prose was correct, so the release-name invariant is a link to the guard now instead of a restatement.
  4. All in, including the pull request template.
  5. Asking on docs(dha): PoC finding — CNPG scale subresource has no selector; reopen actuation mechanism #53 instead of writing it myself, scooby87 owns that argument.
  6. Opened as No home for design principles that bind more than one package cozystack#3930.

One thing neither of us raised: rust and python freeze merged proposals, so supersession is the only way to change anything and the history is automatic. Kubernetes keeps living KEPs like we do and has the same gap, its Implementation History records dates not reasoning. I recorded freezing as a rejected alternative in 0001 because it contradicts "reference, not a binding spec", but it is the real fallback if records don't get written.

Folded #57 in here, it was one line in a file this branch already touches, so closing it.

Design proposals are intent, written before the work. When implementation
contradicts the design the proposal gets revised in place, so it ends up
reading as though it always said the current thing -- and the reasoning
that changed our minds survives only in a pull-request diff.

Add decisions/ for that reasoning: one page per settled decision, never
edited after merge, superseded rather than rewritten. Reviewed for
accuracy by one maintainer rather than by consensus, because the decision
has already been made.

Wire it into the "Where do I file?" table and rewrite Proposal Drift to
say what to do when the implementation diverges.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
The ComputePlane delivery shape was decided three times: a tenant module
with an inline cluster HelmRelease (#17), preset fields on kind: Kubernetes
(#27, closed), and finally an operator-owned extra module wrapping the
unchanged apps/kubernetes (#33). Only the last one is visible in the
proposal today; why the preset-field model lost lives in the #33 body.

Record it as the first decision, and add the Decisions section to the
proposal template so later records get linked from the proposal they
amend.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Records lived in a root-level decisions/ tree with a global sequence. Move
them to design-proposals/<name>/decisions/NNNN-slug.md, fold the process
text into design-proposals/README.md, and put the template at
design-proposals/decision-template.md.

A decision is a decision with respect to something, and the front-door
table routes every candidate that is not a proposal amendment elsewhere —
so a deliberation weighty enough to need a record, with no proposal to
attach it to, has demonstrated that it is a proposal. The template's
"Proposal: ... or none" presupposed a residue that does not exist; the
field is now required.

What follows from the placement: both link directions come for free, so
the proposal's Decisions section stops being load-bearing and the record's
parent directory is its proposal. Numbering collides only when two pull
requests touch the same proposal, rather than constantly against a global
sequence whose renumber-on-rebase rule breaks any citation already
written down. A withdrawn proposal takes its decisions with it instead of
orphaning entries in a global log. And the root README goes back to
routing two document classes instead of three — you never file a decision
on its own, you file a proposal amendment that carries one.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
The record was backfilled from an argument between other people and got
several things wrong. Corrections, all verified against the threads and
the code:

Deciders is @kvaps and @lllamnyp — the author of #33 and the maintainer
who approved it. @myasnikovdaniil's #26 forced the reopening and is now
credited in Context, where it belongs; its literal ask (no new kind at
all) is not what was adopted.

The record never cited #33, the pull request where the decision was
actually argued and merged, and where its 2026-07-18 date comes from. A
record whose job is to be the durable pointer to the reasoning pointed
everywhere except at it. It is now in the new Decided in field.

The #17 attribution was wrong. That review was an approval, and it did
not argue that ComputePlane should not be a distinct kind — that is #26's
argument alone, which #26 made while quoting the approval in support. What
the review did contribute is the tamper-resistance argument (withhold
admin, not visibility), which is the load-bearing rationale of this
record's own Decision and Consequences and was credited to nobody.

Implemented in was "not yet"; cozystack/cozystack#3280 merged 2026-07-29.

The rejection of the preset-field model rested on the fragile argument —
packages/extra/computeplane and the PackageSource convention, both of
which #39 would dissolve. Replaced with the structural one, which
survives it: capabilities and schemas are per-kind, ApplicationDefinition
has no fixed-values facility, the conversion makes the tenant's spec
become the Helm values, and a narrow openAPISchema does not substitute
because a field you can default is a field the tenant can override.

The release-name invariant is now a link to the guard and its test rather
than a restatement. The prose said it constrained moving the chart
between directories; the actual mechanism is that the aggregated API
rebuilds HelmRelease specs without spec.releaseName, which the test's
suite comment states correctly.

Added the dropped consequence: #33 was asked to co-land with #39 or land
after it, because once tenants can set computeplane the invariant becomes
a migration of live Kamaji clusters holding tenant data. #3280 has merged
and #39 is open, so that constraint is live now rather than composing.

Noted that #27 was closed unmerged, superseded by #33 the same day. In a
document that is explicitly a history, that matters.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
The reasoning for why decision records exist and where they live was going
to survive only in the #56 thread — which is precisely the failure the
change is written to fix. It reproduced its own diagnosis on itself.

So it lands as a design proposal, design-proposals/decision-records, with
the drift evidence that was previously only in the pull-request
description, and a record under it for the placement decision. The
proposal states the change and the rationale; the operative rules stay in
design-proposals/README.md, because a merged proposal is a reference and
not a binding spec.

This is not ceremony, on one condition: the record has a losing
alternative. "We will write decision records" has none, and a record for
it would be a form to copy. The placement question has four real arguments
on the other side, and 0001 states them at full strength — the citable
monotonic id, Nygard's tooling, the single directory that lists itself,
and cross-proposal decisions having no home — then says what decided each
one. It also demonstrates per-proposal numbering immediately: two
proposals, two records, both 0001.

Recorded a fifth alternative that the review did not raise and neither did
the original proposal: freezing merged proposals and superseding them, the
Rust RFC and Python PEP model, where the history is automatic because
supersession is the only way to change anything. Rejected — it contradicts
"a reference, not a binding spec" and makes every implementation finding a
new proposal — but it is the model the largest peers picked, and Kubernetes
keeping living KEPs has exactly this gap and fills it with milestone dates
only. A future reader will ask why we did not do that, so the answer is on
the record.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
There is no CI in this repository — .github holds issue templates only —
so the rule in Proposal Drift is only as binding as the place a reviewer
looks. A pull-request template is the one enforcement mechanism available,
and it costs a single file.

One substantive checkbox: if this revises a merged proposal, it adds a
record or says why none is needed. The escape hatch is deliberate and the
template names what legitimately takes it — wording fixes, corrections of
fact, and decisions the code already explains — because a checkbox that
cannot be declined honestly gets ticked without being read.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
The field held a whole paragraph on why the preset-field revision was set
aside. "This PR" stops resolving the moment the next revision lands, so it
was unmaintainable by construction, and Overview directly below it already
said the same thing — a third copy of one rationale with no place of its
own.

Decision 0001 is that place now, so the field becomes Supersedes plus a
link.

This was #57, stacked on this branch. Folded here instead: the change is
one line in a file this branch already edits, so a separate stacked pull
request bought nothing and needed a rebase to survive the move of the
records directory.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Both links to the #17 review carried review id 4959656115, which is the
review on #56 rather than the one on #17. The #17 approval is 4593662101;
they landed on the right pull request with a dead anchor.

Link fix only, permitted on a merged record by the immutability rule; no
prose changed.

Assisted-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>

@lllamnyp Timofei Larkin (lllamnyp) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. Every section of the previous review is addressed, and two things came back better than what was asked for.

The prior-art table is the first: Rust and Python freeze merged proposals, so supersession produces the history for free and no second document class is needed, while Kubernetes keeps living proposals as we do and fills the gap with milestone dates rather than reasoning. That framing wasn't in the review, it's the right way to situate this, and recording the frozen-proposal model as a rejected alternative — labelled as the option the largest peers actually chose, and the live fallback if records don't get written — is more honest than the argument needed to be. Backstage's ADR001 and the GOV.UK Design System's proposal 001 also settle the "is bootstrapping theater" question: introducing the mechanism through the mechanism is how these normally start.

The second is Consequences in the ComputePlane record. Replacing the restatement of the release-name invariant with a link to check-release-name.yaml and release_name_test.yaml is the rule in "What belongs in code instead" being applied to the record that motivated it, which is the strongest evidence the rule works.

I pushed one commit on top: both links to the review on #17 carried review id 4959656115, which is the review on this pull request rather than the one on #17. Corrected to 4593662101. Link fix only, no prose touched.

Two things to fix in a follow-up rather than here.

The #53 bullet in the decision-records proposal is stale. It reads that #53 "reopened that proposal's actuation mechanism … the Cluster /scale subresource exposes no status.selector, and upstream cloudnative-pg#7923 is closed as not planned." #53 was reworked and merged on 2026-08-18 with a different account: cloudnative-pg#7923 is closed not_planned, but as an unnoticed duplicate rather than a decline, and the underlying request was implemented by cloudnative-pg#8996, merged 2026-06-04, which populates Cluster.status.selector and wires labelSelectorPath — shipped in CNPG 1.28.4, 1.29.2 and 1.30.0. The resolution became a version bump rather than a design change, and the "decision needed" framing came out. So #53 no longer illustrates a reopened design, and citing #7923 as declined repeats the reading that #53 itself dropped. The other five precedents in that section are unaffected; this is one bullet.

Worth noting why it matters beyond accuracy: a proposal's Context is prose that ages, which is the argument for records in the first place. This one aged in eight days.

The request for the database-autoscaler record sits on a merged pull request. #53 merged on 2026-08-18, and Rollout items 3 and 4 point readers there for work that is still pending — the record itself, plus extracting that proposal's Why this changed section and spike appendix, both still on main. Participants get the notification, but a merged pull request is not a tracker. An issue would hold it.

Neither blocks merging. Nice work on the restructure — it was a lot to ask of a finished pull request.

Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>

@lllamnyp Timofei Larkin (lllamnyp) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still approved — the #53 bullet is accurate now.

Checked against the corrected account in the database-horizontal-autoscaling proposal on main: the PoC ran on 1.27.x, cloudnative-pg#8996 populates Cluster.status.selector and wires labelSelectorPath, and it shipped in v1.28.4, v1.29.2 and v1.30.0 without a 1.27 backport. Dropping the #7923 closed as not planned claim is the right call — the label was accurate but the inference wasn't, and #2574/#8996 is the work that actually landed. "Revised that proposal again … the resolution was a version bump rather than a design change" is also the honest framing; it keeps the bullet in the evidence list without overstating what #53 reopened.

The other follow-up still stands and still isn't blocking: Rollout items 3 and 4 point at #53, which merged on 2026-08-18, for work that hasn't happened yet — the record itself, and extracting that proposal's Why this changed section and spike appendix. Worth an issue so it doesn't rely on a notification from a closed thread.

@myasnikovdaniil
myasnikovdaniil merged commit cb92c73 into main Aug 21, 2026
2 checks passed
myasnikovdaniil added a commit that referenced this pull request Aug 21, 2026
The reasoning for why decision records exist and where they live was going
to survive only in the #56 thread — which is precisely the failure the
change is written to fix. It reproduced its own diagnosis on itself.

So it lands as a design proposal, design-proposals/decision-records, with
the drift evidence that was previously only in the pull-request
description, and a record under it for the placement decision. The
proposal states the change and the rationale; the operative rules stay in
design-proposals/README.md, because a merged proposal is a reference and
not a binding spec.

This is not ceremony, on one condition: the record has a losing
alternative. "We will write decision records" has none, and a record for
it would be a form to copy. The placement question has four real arguments
on the other side, and 0001 states them at full strength — the citable
monotonic id, Nygard's tooling, the single directory that lists itself,
and cross-proposal decisions having no home — then says what decided each
one. It also demonstrates per-proposal numbering immediately: two
proposals, two records, both 0001.

Recorded a fifth alternative that the review did not raise and neither did
the original proposal: freezing merged proposals and superseding them, the
Rust RFC and Python PEP model, where the history is automatic because
supersession is the only way to change anything. Rejected — it contradicts
"a reference, not a binding spec" and makes every implementation finding a
new proposal — but it is the model the largest peers picked, and Kubernetes
keeping living KEPs has exactly this gap and fills it with milestone dates
only. A future reader will ask why we did not do that, so the answer is on
the record.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
myasnikovdaniil pushed a commit that referenced this pull request Aug 21, 2026
Both links to the #17 review carried review id 4959656115, which is the
review on #56 rather than the one on #17. The #17 approval is 4593662101;
they landed on the right pull request with a dead anchor.

Link fix only, permitted on a merged record by the immutability rule; no
prose changed.

Assisted-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants