chipgraph 0.1.0__py3-none-any.whl
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.
- chipgraph/__init__.py +7 -0
- chipgraph/adapters/__init__.py +1 -0
- chipgraph/adapters/format/__init__.py +6 -0
- chipgraph/adapters/format/__main__.py +16 -0
- chipgraph/adapters/format/_yaml_lines.py +54 -0
- chipgraph/adapters/format/chip_yaml.py +323 -0
- chipgraph/adapters/format/qsoc_contract.py +246 -0
- chipgraph/adapters/llm/__init__.py +58 -0
- chipgraph/adapters/llm/_budget.py +173 -0
- chipgraph/adapters/llm/_errors.py +50 -0
- chipgraph/adapters/llm/_nda.py +30 -0
- chipgraph/adapters/llm/_retry.py +85 -0
- chipgraph/adapters/llm/anthropic.py +247 -0
- chipgraph/adapters/llm/decide_backend.py +179 -0
- chipgraph/adapters/llm/fake.py +87 -0
- chipgraph/adapters/llm/guard.py +68 -0
- chipgraph/adapters/parser/__init__.py +22 -0
- chipgraph/adapters/parser/cocotb.py +304 -0
- chipgraph/adapters/parser/generic_regex.py +75 -0
- chipgraph/adapters/parser/verible.py +55 -0
- chipgraph/adapters/parser/verilator.py +46 -0
- chipgraph/adapters/review/__init__.py +7 -0
- chipgraph/adapters/review/file.py +123 -0
- chipgraph/adapters/review/github.py +886 -0
- chipgraph/adapters/runner/__init__.py +7 -0
- chipgraph/adapters/runner/local.py +122 -0
- chipgraph/adapters/runtime/__init__.py +5 -0
- chipgraph/adapters/runtime/claude_code/__init__.py +24 -0
- chipgraph/adapters/runtime/claude_code/agents.py +158 -0
- chipgraph/adapters/runtime/claude_code/decisions.py +280 -0
- chipgraph/adapters/runtime/claude_code/guarded.py +310 -0
- chipgraph/adapters/runtime/claude_code/isolation.py +482 -0
- chipgraph/adapters/runtime/claude_code/loop.py +342 -0
- chipgraph/adapters/runtime/claude_code/previous.py +419 -0
- chipgraph/adapters/runtime/claude_code/review.py +492 -0
- chipgraph/adapters/runtime/claude_code/runtime.py +192 -0
- chipgraph/adapters/runtime/claude_code/service.py +2285 -0
- chipgraph/adapters/tool/__init__.py +7 -0
- chipgraph/adapters/tool/cmd.py +229 -0
- chipgraph/adapters/tool/edalize.py +773 -0
- chipgraph/adapters/tool/filelist.py +259 -0
- chipgraph/adapters/tool/pyslang.py +485 -0
- chipgraph/adapters/vcs/__init__.py +1 -0
- chipgraph/adapters/vcs/git.py +521 -0
- chipgraph/app/__init__.py +10 -0
- chipgraph/app/baseline.py +339 -0
- chipgraph/app/build.py +332 -0
- chipgraph/app/checks.py +202 -0
- chipgraph/app/context.py +191 -0
- chipgraph/app/errors.py +11 -0
- chipgraph/app/executors.py +79 -0
- chipgraph/app/findings.py +203 -0
- chipgraph/app/ingest.py +523 -0
- chipgraph/checks/__init__.py +52 -0
- chipgraph/checks/_cdc_analyze.py +272 -0
- chipgraph/checks/_cdc_elaborate.py +455 -0
- chipgraph/checks/_common.py +268 -0
- chipgraph/checks/_cross.py +96 -0
- chipgraph/checks/_model.py +64 -0
- chipgraph/checks/_naming_pyslang.py +235 -0
- chipgraph/checks/_naming_rules.py +127 -0
- chipgraph/checks/_xref_syntax.py +260 -0
- chipgraph/checks/cdc_struct.py +235 -0
- chipgraph/checks/connect.py +353 -0
- chipgraph/checks/cross_chip.py +454 -0
- chipgraph/checks/duplicate.py +153 -0
- chipgraph/checks/filelist.py +323 -0
- chipgraph/checks/format.py +268 -0
- chipgraph/checks/generated.py +151 -0
- chipgraph/checks/hardcode.py +233 -0
- chipgraph/checks/layout.py +155 -0
- chipgraph/checks/naming.py +311 -0
- chipgraph/checks/plan_check.py +901 -0
- chipgraph/checks/ports_diff.py +203 -0
- chipgraph/checks/spec_schema.py +277 -0
- chipgraph/checks/style.py +910 -0
- chipgraph/checks/tb_static.py +599 -0
- chipgraph/checks/trace.py +224 -0
- chipgraph/checks/vendor_guard.py +369 -0
- chipgraph/checks/wrap_check.py +486 -0
- chipgraph/cli/__init__.py +1688 -0
- chipgraph/cli/next_steps.py +137 -0
- chipgraph/core/__init__.py +1 -0
- chipgraph/core/config/__init__.py +93 -0
- chipgraph/core/config/defaults.py +14 -0
- chipgraph/core/config/errors.py +32 -0
- chipgraph/core/config/loader.py +766 -0
- chipgraph/core/config/models.py +845 -0
- chipgraph/core/config/templates.py +340 -0
- chipgraph/core/contracts/__init__.py +75 -0
- chipgraph/core/contracts/_paths.py +26 -0
- chipgraph/core/contracts/agent.py +49 -0
- chipgraph/core/contracts/artifact.py +68 -0
- chipgraph/core/contracts/check.py +76 -0
- chipgraph/core/contracts/event.py +70 -0
- chipgraph/core/contracts/export.py +128 -0
- chipgraph/core/contracts/finding.py +171 -0
- chipgraph/core/contracts/gate.py +50 -0
- chipgraph/core/contracts/rule.py +177 -0
- chipgraph/core/contracts/types.py +45 -0
- chipgraph/core/engine/__init__.py +70 -0
- chipgraph/core/engine/agent_rule.py +1362 -0
- chipgraph/core/engine/baseline.py +283 -0
- chipgraph/core/engine/decide.py +462 -0
- chipgraph/core/engine/fanout.py +1358 -0
- chipgraph/core/engine/gate.py +170 -0
- chipgraph/core/engine/graph.py +647 -0
- chipgraph/core/engine/records.py +60 -0
- chipgraph/core/engine/rules.py +92 -0
- chipgraph/core/engine/scheduler.py +637 -0
- chipgraph/core/engine/select.py +1239 -0
- chipgraph/core/engine/targets.py +342 -0
- chipgraph/core/model/__init__.py +98 -0
- chipgraph/core/model/entities.py +244 -0
- chipgraph/core/model/export.py +16 -0
- chipgraph/core/model/ingest.py +693 -0
- chipgraph/core/model/json_value.py +4 -0
- chipgraph/core/model/keys.py +95 -0
- chipgraph/core/model/model.py +153 -0
- chipgraph/core/model/provenance.py +27 -0
- chipgraph/core/model/query.py +583 -0
- chipgraph/core/model/registry.py +80 -0
- chipgraph/core/model/relations.py +61 -0
- chipgraph/core/model/store.py +279 -0
- chipgraph/core/plugin_api/__init__.py +81 -0
- chipgraph/core/plugin_api/local.py +167 -0
- chipgraph/core/plugin_api/pack.py +159 -0
- chipgraph/core/plugin_api/protocols.py +177 -0
- chipgraph/core/plugin_api/registry.py +111 -0
- chipgraph/core/plugin_api/types.py +94 -0
- chipgraph/core/runtime/__init__.py +41 -0
- chipgraph/core/runtime/executor.py +79 -0
- chipgraph/core/runtime/queue.py +565 -0
- chipgraph/core/runtime/roles/__init__.py +91 -0
- chipgraph/core/runtime/roles/data/author.md +41 -0
- chipgraph/core/runtime/roles/data/critic.md +56 -0
- chipgraph/core/runtime/roles/data/planner.md +38 -0
- chipgraph/core/runtime/roles/data/researcher.md +37 -0
- chipgraph/core/runtime/roles/data/rtl-author.md +64 -0
- chipgraph/core/runtime/roles/data/tb-author.md +48 -0
- chipgraph/core/runtime/roles/data/triage.md +34 -0
- chipgraph/core/runtime/roles/policy.py +299 -0
- chipgraph/core/runtime/roles/registry.py +52 -0
- chipgraph/core/runtime/roles/review.py +275 -0
- chipgraph/core/runtime/roles/schema.py +82 -0
- chipgraph/core/runtime/roles/skills.py +134 -0
- chipgraph/core/runtime/roles/spec.py +232 -0
- chipgraph/core/state/__init__.py +1 -0
- chipgraph/core/state/artifacts.py +156 -0
- chipgraph/core/state/backend.py +117 -0
- chipgraph/core/state/findings.py +151 -0
- chipgraph/core/state/handoff.py +411 -0
- chipgraph/core/state/idempotency.py +59 -0
- chipgraph/core/state/journal.py +238 -0
- chipgraph/core/state/layout.py +82 -0
- chipgraph/core/state/lock.py +195 -0
- chipgraph/core/state/trace.py +225 -0
- chipgraph/learn/__init__.py +96 -0
- chipgraph/learn/draft.py +187 -0
- chipgraph/learn/infer.py +580 -0
- chipgraph/learn/models.py +145 -0
- chipgraph/learn/try_run.py +228 -0
- chipgraph/mcp/__init__.py +12 -0
- chipgraph/mcp/model_tools.py +167 -0
- chipgraph/mcp/server.py +787 -0
- chipgraph/orgs/qnsc/naming-v1.yml +113 -0
- chipgraph/orgs/qnsc/profile.yml +5 -0
- chipgraph/packs/__init__.py +8 -0
- chipgraph/packs/assist/__init__.py +16 -0
- chipgraph/packs/assist/ask/__init__.py +58 -0
- chipgraph/packs/assist/ask/_db.py +57 -0
- chipgraph/packs/assist/ask/_project.py +230 -0
- chipgraph/packs/assist/ask/answer.py +266 -0
- chipgraph/packs/assist/ask/check.py +182 -0
- chipgraph/packs/assist/ask/contract.py +115 -0
- chipgraph/packs/assist/ask/documents.py +157 -0
- chipgraph/packs/assist/ask/fts.py +80 -0
- chipgraph/packs/assist/ask/retrieve.py +331 -0
- chipgraph/packs/assist/audit/__init__.py +383 -0
- chipgraph/packs/assist/pack.yml +13 -0
- chipgraph/packs/assist/triage/__init__.py +64 -0
- chipgraph/packs/assist/triage/contract.py +94 -0
- chipgraph/packs/assist/triage/parse.py +371 -0
- chipgraph/packs/assist/triage/question.py +384 -0
- chipgraph/packs/assist/triage/report.py +160 -0
- chipgraph/packs/assist/triage/rules.py +447 -0
- chipgraph/packs/assist/triage/run.py +224 -0
- chipgraph/packs/digital_rtl/__init__.py +19 -0
- chipgraph/packs/digital_rtl/layout.py +144 -0
- chipgraph/packs/digital_rtl/pack.yml +25 -0
- chipgraph/packs/digital_rtl/plan/__init__.py +34 -0
- chipgraph/packs/digital_rtl/plan/__main__.py +16 -0
- chipgraph/packs/digital_rtl/plan/context.py +82 -0
- chipgraph/packs/digital_rtl/plan/expand.py +94 -0
- chipgraph/packs/digital_rtl/plan/model.py +150 -0
- chipgraph/packs/digital_rtl/plan/resolver.py +400 -0
- chipgraph/packs/digital_rtl/plan/show.py +176 -0
- chipgraph/packs/digital_rtl/pr/__init__.py +11 -0
- chipgraph/packs/digital_rtl/pr/body.py +165 -0
- chipgraph/packs/digital_rtl/pr/gate.py +110 -0
- chipgraph/packs/digital_rtl/pr/open.py +397 -0
- chipgraph/packs/digital_rtl/rtl/__init__.py +9 -0
- chipgraph/packs/digital_rtl/rtl/context.py +272 -0
- chipgraph/packs/digital_rtl/rules/open_pr.yml +30 -0
- chipgraph/packs/digital_rtl/rules/plan.yml +28 -0
- chipgraph/packs/digital_rtl/rules/plan_expand.yml +29 -0
- chipgraph/packs/digital_rtl/rules/pr_gate.yml +27 -0
- chipgraph/packs/digital_rtl/rules/review.yml +31 -0
- chipgraph/packs/digital_rtl/rules/rtl_module.yml +53 -0
- chipgraph/packs/digital_rtl/skills/lang-sv-rtl.md +58 -0
- chipgraph/packs/digital_rtl/skills/org-naming.md +29 -0
- chipgraph/packs/digital_rtl/skills/plan-modules.md +63 -0
- chipgraph/packs/digital_rtl/skills/review-diff.md +41 -0
- chipgraph/packs/digital_rtl/targets/plan.yml +11 -0
- chipgraph/packs/digital_rtl/targets/rtl.yml +20 -0
- chipgraph/packs/digital_rtl/wrapper/__init__.py +24 -0
- chipgraph/packs/digital_rtl/wrapper/common.py +171 -0
- chipgraph/packs/digital_rtl/wrapper/context.py +222 -0
- chipgraph/packs/digital_rtl/wrapper/emacs.py +626 -0
- chipgraph/packs/digital_rtl/wrapper/expansion.py +304 -0
- chipgraph/packs/digital_rtl/wrapper/lisp.py +504 -0
- chipgraph/packs/digital_rtl/wrapper/resolver.py +60 -0
- chipgraph/packs/digital_rtl/wrapper/rules/wrapper.yml +41 -0
- chipgraph/packs/digital_rtl/wrapper/rules/wrapper_gen.yml +35 -0
- chipgraph/packs/digital_rtl/wrapper/skills/wrapper-auto.md +120 -0
- chipgraph/packs/dv/__init__.py +15 -0
- chipgraph/packs/dv/declaration.py +392 -0
- chipgraph/packs/dv/interface.py +596 -0
- chipgraph/packs/dv/pack.yml +14 -0
- chipgraph/packs/dv/rules/tb_module.yml +42 -0
- chipgraph/packs/dv/skills/dv/cocotb.md +72 -0
- chipgraph/packs/spec_core/__init__.py +9 -0
- chipgraph/packs/spec_core/extract/__init__.py +12 -0
- chipgraph/packs/spec_core/extract/_markdown.py +371 -0
- chipgraph/packs/spec_core/extract/_text.py +116 -0
- chipgraph/packs/spec_core/extract/mas.py +975 -0
- chipgraph/packs/spec_core/gen/__init__.py +11 -0
- chipgraph/packs/spec_core/gen/agents_md/__init__.py +148 -0
- chipgraph/packs/spec_core/gen/agents_md/_context.py +239 -0
- chipgraph/packs/spec_core/gen/agents_md/_markers.py +131 -0
- chipgraph/packs/spec_core/gen/agents_md/agents_md.md.j2 +178 -0
- chipgraph/packs/spec_core/gen/diagram/__init__.py +110 -0
- chipgraph/packs/spec_core/gen/diagram/__main__.py +44 -0
- chipgraph/packs/spec_core/gen/diagram/_diagrams.py +403 -0
- chipgraph/packs/spec_core/gen/diagram/_scene.py +163 -0
- chipgraph/packs/spec_core/pack.yml +6 -0
- chipgraph/py.typed +0 -0
- chipgraph-0.1.0.dist-info/METADATA +53 -0
- chipgraph-0.1.0.dist-info/RECORD +252 -0
- chipgraph-0.1.0.dist-info/WHEEL +4 -0
- chipgraph-0.1.0.dist-info/entry_points.txt +61 -0
- chipgraph-0.1.0.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""Gates: whether a rule instance's gate is approved, rejected, or waiting.
|
|
2
|
+
|
|
3
|
+
A gate covers a rule instance's *file-based* inputs: the things a person actually
|
|
4
|
+
reviewed. An approval is only current when every one of those hashes still matches
|
|
5
|
+
what is on disk today *and* the approval's hashes cover every current input (an
|
|
6
|
+
approval recorded against a smaller, older input set does not count as covering a
|
|
7
|
+
new one). This is what makes "edit a file after approval" put the gate back to
|
|
8
|
+
`waiting` (DESIGN.md 6.1).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from collections.abc import Collection, Iterable, Mapping
|
|
14
|
+
from datetime import UTC, datetime
|
|
15
|
+
from typing import Literal
|
|
16
|
+
|
|
17
|
+
from chipgraph.core.contracts import Approval, ArtifactRef, RuleInstance
|
|
18
|
+
from chipgraph.core.plugin_api.protocols import ReviewAdapter
|
|
19
|
+
from chipgraph.core.state.artifacts import ArtifactStore
|
|
20
|
+
|
|
21
|
+
GateStatus = Literal["approved", "rejected", "waiting"]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class GateError(Exception):
|
|
25
|
+
"""Raised for gate id formatting errors, and invalid approve/baseline calls."""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def gate_id_for(template: str, params: Mapping[str, str]) -> str:
|
|
29
|
+
"""Format a gate id template, e.g. `"spec:{block}"` with `{"block": "timer"}`.
|
|
30
|
+
|
|
31
|
+
Raises `GateError` if `template` references a param that is not in `params`.
|
|
32
|
+
"""
|
|
33
|
+
try:
|
|
34
|
+
return template.format(**params)
|
|
35
|
+
except KeyError as exc:
|
|
36
|
+
raise GateError(
|
|
37
|
+
f"gate id template {template!r} needs param {exc.args[0]!r}, "
|
|
38
|
+
f"which is not in {dict(params)!r}"
|
|
39
|
+
) from exc
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _file_inputs(instance: RuleInstance) -> tuple[ArtifactRef, ...]:
|
|
43
|
+
return tuple(ref for ref in instance.inputs if ref.path is not None)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class GateEvaluator:
|
|
47
|
+
"""Evaluates gate status for the scheduler's `GateChecker` shape (DESIGN.md 6.1).
|
|
48
|
+
|
|
49
|
+
`pr_gates` names gate id prefixes (the part before `:`) whose approval is meant
|
|
50
|
+
to come from a code review platform's own PR review, not a decision file. Until
|
|
51
|
+
the `github` review adapter (M2-10) exists, a PR gate here is `approved` only
|
|
52
|
+
when a current `approve` decision has been recorded through `review` for it
|
|
53
|
+
(there is no PR-status source yet), and `waiting` otherwise; it is never derived
|
|
54
|
+
from a real PR's review state.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(
|
|
58
|
+
self,
|
|
59
|
+
review: ReviewAdapter,
|
|
60
|
+
store: ArtifactStore,
|
|
61
|
+
*,
|
|
62
|
+
pr_gates: Collection[str] = (),
|
|
63
|
+
) -> None:
|
|
64
|
+
self.review = review
|
|
65
|
+
self.store = store
|
|
66
|
+
self.pr_gates = frozenset(pr_gates)
|
|
67
|
+
|
|
68
|
+
def status(self, gate_id: str, instance: RuleInstance) -> GateStatus:
|
|
69
|
+
"""Return the current status of `gate_id` for `instance`.
|
|
70
|
+
|
|
71
|
+
`gate_id` is assumed already formatted with `instance`'s params (the
|
|
72
|
+
scheduler does that with `gate_id_for`). The gate's covered artifacts are
|
|
73
|
+
`instance`'s file-based inputs.
|
|
74
|
+
"""
|
|
75
|
+
current = self.store.current_hashes(_file_inputs(instance))
|
|
76
|
+
decisions = self._current_decisions(gate_id, current)
|
|
77
|
+
|
|
78
|
+
prefix = gate_id.split(":", 1)[0]
|
|
79
|
+
if prefix in self.pr_gates:
|
|
80
|
+
has_approve = any(decision.decision == "approve" for decision in decisions)
|
|
81
|
+
return "approved" if has_approve else "waiting"
|
|
82
|
+
|
|
83
|
+
if not decisions:
|
|
84
|
+
return "waiting"
|
|
85
|
+
latest = decisions[-1]
|
|
86
|
+
if latest.decision in ("approve", "baseline"):
|
|
87
|
+
return "approved"
|
|
88
|
+
if latest.decision == "reject":
|
|
89
|
+
return "rejected"
|
|
90
|
+
return "waiting"
|
|
91
|
+
|
|
92
|
+
def _current_decisions(self, gate_id: str, current: Mapping[str, str]) -> list[Approval]:
|
|
93
|
+
current_keys = frozenset(current)
|
|
94
|
+
decisions = [
|
|
95
|
+
approval
|
|
96
|
+
for approval in self.review.approvals(gate_id)
|
|
97
|
+
if approval.decision != "waive"
|
|
98
|
+
and approval.is_current(current)
|
|
99
|
+
and current_keys.issubset(approval.artifact_hashes.keys())
|
|
100
|
+
]
|
|
101
|
+
decisions.sort(key=lambda approval: approval.at)
|
|
102
|
+
return decisions
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def approve(
|
|
106
|
+
review: ReviewAdapter,
|
|
107
|
+
store: ArtifactStore,
|
|
108
|
+
gate_id: str,
|
|
109
|
+
instance: RuleInstance,
|
|
110
|
+
*,
|
|
111
|
+
by: str,
|
|
112
|
+
decision: Literal["approve", "reject"] = "approve",
|
|
113
|
+
note: str = "",
|
|
114
|
+
) -> Approval:
|
|
115
|
+
"""Record an approve/reject decision for `gate_id` over `instance`'s file inputs.
|
|
116
|
+
|
|
117
|
+
Backs `chipgraph approve` (CLI in M0-12). Raises `GateError` if `instance` has no
|
|
118
|
+
file-based inputs (there is nothing on disk to pin the decision's hashes to).
|
|
119
|
+
"""
|
|
120
|
+
file_inputs = _file_inputs(instance)
|
|
121
|
+
if not file_inputs:
|
|
122
|
+
raise GateError(
|
|
123
|
+
f"cannot record a decision for gate {gate_id!r}: instance "
|
|
124
|
+
f"{instance.instance_id!r} has no file-based inputs"
|
|
125
|
+
)
|
|
126
|
+
hashes = store.current_hashes(file_inputs)
|
|
127
|
+
if not hashes:
|
|
128
|
+
raise GateError(
|
|
129
|
+
f"cannot record a decision for gate {gate_id!r}: none of instance "
|
|
130
|
+
f"{instance.instance_id!r}'s file inputs exist on disk"
|
|
131
|
+
)
|
|
132
|
+
approval = Approval(
|
|
133
|
+
gate_id=gate_id,
|
|
134
|
+
by=by,
|
|
135
|
+
at=datetime.now(UTC),
|
|
136
|
+
artifact_hashes=hashes,
|
|
137
|
+
decision=decision,
|
|
138
|
+
note=note,
|
|
139
|
+
)
|
|
140
|
+
review.record(approval)
|
|
141
|
+
return approval
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def baseline(
|
|
145
|
+
review: ReviewAdapter,
|
|
146
|
+
store: ArtifactStore,
|
|
147
|
+
gate_id: str,
|
|
148
|
+
refs: Iterable[ArtifactRef],
|
|
149
|
+
*,
|
|
150
|
+
by: str,
|
|
151
|
+
note: str = "",
|
|
152
|
+
) -> Approval:
|
|
153
|
+
"""Record a `baseline` decision over `refs`' current hashes (DESIGN.md 6.4).
|
|
154
|
+
|
|
155
|
+
The full `chipgraph baseline` command arrives in M1-24; this is the primitive it
|
|
156
|
+
calls once per gate. Raises `GateError` if none of `refs` exist on disk.
|
|
157
|
+
"""
|
|
158
|
+
hashes = store.current_hashes(refs)
|
|
159
|
+
if not hashes:
|
|
160
|
+
raise GateError(f"cannot baseline gate {gate_id!r}: none of the given refs exist on disk")
|
|
161
|
+
approval = Approval(
|
|
162
|
+
gate_id=gate_id,
|
|
163
|
+
by=by,
|
|
164
|
+
at=datetime.now(UTC),
|
|
165
|
+
artifact_hashes=hashes,
|
|
166
|
+
decision="baseline",
|
|
167
|
+
note=note,
|
|
168
|
+
)
|
|
169
|
+
review.record(approval)
|
|
170
|
+
return approval
|