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.
Files changed (252) hide show
  1. chipgraph/__init__.py +7 -0
  2. chipgraph/adapters/__init__.py +1 -0
  3. chipgraph/adapters/format/__init__.py +6 -0
  4. chipgraph/adapters/format/__main__.py +16 -0
  5. chipgraph/adapters/format/_yaml_lines.py +54 -0
  6. chipgraph/adapters/format/chip_yaml.py +323 -0
  7. chipgraph/adapters/format/qsoc_contract.py +246 -0
  8. chipgraph/adapters/llm/__init__.py +58 -0
  9. chipgraph/adapters/llm/_budget.py +173 -0
  10. chipgraph/adapters/llm/_errors.py +50 -0
  11. chipgraph/adapters/llm/_nda.py +30 -0
  12. chipgraph/adapters/llm/_retry.py +85 -0
  13. chipgraph/adapters/llm/anthropic.py +247 -0
  14. chipgraph/adapters/llm/decide_backend.py +179 -0
  15. chipgraph/adapters/llm/fake.py +87 -0
  16. chipgraph/adapters/llm/guard.py +68 -0
  17. chipgraph/adapters/parser/__init__.py +22 -0
  18. chipgraph/adapters/parser/cocotb.py +304 -0
  19. chipgraph/adapters/parser/generic_regex.py +75 -0
  20. chipgraph/adapters/parser/verible.py +55 -0
  21. chipgraph/adapters/parser/verilator.py +46 -0
  22. chipgraph/adapters/review/__init__.py +7 -0
  23. chipgraph/adapters/review/file.py +123 -0
  24. chipgraph/adapters/review/github.py +886 -0
  25. chipgraph/adapters/runner/__init__.py +7 -0
  26. chipgraph/adapters/runner/local.py +122 -0
  27. chipgraph/adapters/runtime/__init__.py +5 -0
  28. chipgraph/adapters/runtime/claude_code/__init__.py +24 -0
  29. chipgraph/adapters/runtime/claude_code/agents.py +158 -0
  30. chipgraph/adapters/runtime/claude_code/decisions.py +280 -0
  31. chipgraph/adapters/runtime/claude_code/guarded.py +310 -0
  32. chipgraph/adapters/runtime/claude_code/isolation.py +482 -0
  33. chipgraph/adapters/runtime/claude_code/loop.py +342 -0
  34. chipgraph/adapters/runtime/claude_code/previous.py +419 -0
  35. chipgraph/adapters/runtime/claude_code/review.py +492 -0
  36. chipgraph/adapters/runtime/claude_code/runtime.py +192 -0
  37. chipgraph/adapters/runtime/claude_code/service.py +2285 -0
  38. chipgraph/adapters/tool/__init__.py +7 -0
  39. chipgraph/adapters/tool/cmd.py +229 -0
  40. chipgraph/adapters/tool/edalize.py +773 -0
  41. chipgraph/adapters/tool/filelist.py +259 -0
  42. chipgraph/adapters/tool/pyslang.py +485 -0
  43. chipgraph/adapters/vcs/__init__.py +1 -0
  44. chipgraph/adapters/vcs/git.py +521 -0
  45. chipgraph/app/__init__.py +10 -0
  46. chipgraph/app/baseline.py +339 -0
  47. chipgraph/app/build.py +332 -0
  48. chipgraph/app/checks.py +202 -0
  49. chipgraph/app/context.py +191 -0
  50. chipgraph/app/errors.py +11 -0
  51. chipgraph/app/executors.py +79 -0
  52. chipgraph/app/findings.py +203 -0
  53. chipgraph/app/ingest.py +523 -0
  54. chipgraph/checks/__init__.py +52 -0
  55. chipgraph/checks/_cdc_analyze.py +272 -0
  56. chipgraph/checks/_cdc_elaborate.py +455 -0
  57. chipgraph/checks/_common.py +268 -0
  58. chipgraph/checks/_cross.py +96 -0
  59. chipgraph/checks/_model.py +64 -0
  60. chipgraph/checks/_naming_pyslang.py +235 -0
  61. chipgraph/checks/_naming_rules.py +127 -0
  62. chipgraph/checks/_xref_syntax.py +260 -0
  63. chipgraph/checks/cdc_struct.py +235 -0
  64. chipgraph/checks/connect.py +353 -0
  65. chipgraph/checks/cross_chip.py +454 -0
  66. chipgraph/checks/duplicate.py +153 -0
  67. chipgraph/checks/filelist.py +323 -0
  68. chipgraph/checks/format.py +268 -0
  69. chipgraph/checks/generated.py +151 -0
  70. chipgraph/checks/hardcode.py +233 -0
  71. chipgraph/checks/layout.py +155 -0
  72. chipgraph/checks/naming.py +311 -0
  73. chipgraph/checks/plan_check.py +901 -0
  74. chipgraph/checks/ports_diff.py +203 -0
  75. chipgraph/checks/spec_schema.py +277 -0
  76. chipgraph/checks/style.py +910 -0
  77. chipgraph/checks/tb_static.py +599 -0
  78. chipgraph/checks/trace.py +224 -0
  79. chipgraph/checks/vendor_guard.py +369 -0
  80. chipgraph/checks/wrap_check.py +486 -0
  81. chipgraph/cli/__init__.py +1688 -0
  82. chipgraph/cli/next_steps.py +137 -0
  83. chipgraph/core/__init__.py +1 -0
  84. chipgraph/core/config/__init__.py +93 -0
  85. chipgraph/core/config/defaults.py +14 -0
  86. chipgraph/core/config/errors.py +32 -0
  87. chipgraph/core/config/loader.py +766 -0
  88. chipgraph/core/config/models.py +845 -0
  89. chipgraph/core/config/templates.py +340 -0
  90. chipgraph/core/contracts/__init__.py +75 -0
  91. chipgraph/core/contracts/_paths.py +26 -0
  92. chipgraph/core/contracts/agent.py +49 -0
  93. chipgraph/core/contracts/artifact.py +68 -0
  94. chipgraph/core/contracts/check.py +76 -0
  95. chipgraph/core/contracts/event.py +70 -0
  96. chipgraph/core/contracts/export.py +128 -0
  97. chipgraph/core/contracts/finding.py +171 -0
  98. chipgraph/core/contracts/gate.py +50 -0
  99. chipgraph/core/contracts/rule.py +177 -0
  100. chipgraph/core/contracts/types.py +45 -0
  101. chipgraph/core/engine/__init__.py +70 -0
  102. chipgraph/core/engine/agent_rule.py +1362 -0
  103. chipgraph/core/engine/baseline.py +283 -0
  104. chipgraph/core/engine/decide.py +462 -0
  105. chipgraph/core/engine/fanout.py +1358 -0
  106. chipgraph/core/engine/gate.py +170 -0
  107. chipgraph/core/engine/graph.py +647 -0
  108. chipgraph/core/engine/records.py +60 -0
  109. chipgraph/core/engine/rules.py +92 -0
  110. chipgraph/core/engine/scheduler.py +637 -0
  111. chipgraph/core/engine/select.py +1239 -0
  112. chipgraph/core/engine/targets.py +342 -0
  113. chipgraph/core/model/__init__.py +98 -0
  114. chipgraph/core/model/entities.py +244 -0
  115. chipgraph/core/model/export.py +16 -0
  116. chipgraph/core/model/ingest.py +693 -0
  117. chipgraph/core/model/json_value.py +4 -0
  118. chipgraph/core/model/keys.py +95 -0
  119. chipgraph/core/model/model.py +153 -0
  120. chipgraph/core/model/provenance.py +27 -0
  121. chipgraph/core/model/query.py +583 -0
  122. chipgraph/core/model/registry.py +80 -0
  123. chipgraph/core/model/relations.py +61 -0
  124. chipgraph/core/model/store.py +279 -0
  125. chipgraph/core/plugin_api/__init__.py +81 -0
  126. chipgraph/core/plugin_api/local.py +167 -0
  127. chipgraph/core/plugin_api/pack.py +159 -0
  128. chipgraph/core/plugin_api/protocols.py +177 -0
  129. chipgraph/core/plugin_api/registry.py +111 -0
  130. chipgraph/core/plugin_api/types.py +94 -0
  131. chipgraph/core/runtime/__init__.py +41 -0
  132. chipgraph/core/runtime/executor.py +79 -0
  133. chipgraph/core/runtime/queue.py +565 -0
  134. chipgraph/core/runtime/roles/__init__.py +91 -0
  135. chipgraph/core/runtime/roles/data/author.md +41 -0
  136. chipgraph/core/runtime/roles/data/critic.md +56 -0
  137. chipgraph/core/runtime/roles/data/planner.md +38 -0
  138. chipgraph/core/runtime/roles/data/researcher.md +37 -0
  139. chipgraph/core/runtime/roles/data/rtl-author.md +64 -0
  140. chipgraph/core/runtime/roles/data/tb-author.md +48 -0
  141. chipgraph/core/runtime/roles/data/triage.md +34 -0
  142. chipgraph/core/runtime/roles/policy.py +299 -0
  143. chipgraph/core/runtime/roles/registry.py +52 -0
  144. chipgraph/core/runtime/roles/review.py +275 -0
  145. chipgraph/core/runtime/roles/schema.py +82 -0
  146. chipgraph/core/runtime/roles/skills.py +134 -0
  147. chipgraph/core/runtime/roles/spec.py +232 -0
  148. chipgraph/core/state/__init__.py +1 -0
  149. chipgraph/core/state/artifacts.py +156 -0
  150. chipgraph/core/state/backend.py +117 -0
  151. chipgraph/core/state/findings.py +151 -0
  152. chipgraph/core/state/handoff.py +411 -0
  153. chipgraph/core/state/idempotency.py +59 -0
  154. chipgraph/core/state/journal.py +238 -0
  155. chipgraph/core/state/layout.py +82 -0
  156. chipgraph/core/state/lock.py +195 -0
  157. chipgraph/core/state/trace.py +225 -0
  158. chipgraph/learn/__init__.py +96 -0
  159. chipgraph/learn/draft.py +187 -0
  160. chipgraph/learn/infer.py +580 -0
  161. chipgraph/learn/models.py +145 -0
  162. chipgraph/learn/try_run.py +228 -0
  163. chipgraph/mcp/__init__.py +12 -0
  164. chipgraph/mcp/model_tools.py +167 -0
  165. chipgraph/mcp/server.py +787 -0
  166. chipgraph/orgs/qnsc/naming-v1.yml +113 -0
  167. chipgraph/orgs/qnsc/profile.yml +5 -0
  168. chipgraph/packs/__init__.py +8 -0
  169. chipgraph/packs/assist/__init__.py +16 -0
  170. chipgraph/packs/assist/ask/__init__.py +58 -0
  171. chipgraph/packs/assist/ask/_db.py +57 -0
  172. chipgraph/packs/assist/ask/_project.py +230 -0
  173. chipgraph/packs/assist/ask/answer.py +266 -0
  174. chipgraph/packs/assist/ask/check.py +182 -0
  175. chipgraph/packs/assist/ask/contract.py +115 -0
  176. chipgraph/packs/assist/ask/documents.py +157 -0
  177. chipgraph/packs/assist/ask/fts.py +80 -0
  178. chipgraph/packs/assist/ask/retrieve.py +331 -0
  179. chipgraph/packs/assist/audit/__init__.py +383 -0
  180. chipgraph/packs/assist/pack.yml +13 -0
  181. chipgraph/packs/assist/triage/__init__.py +64 -0
  182. chipgraph/packs/assist/triage/contract.py +94 -0
  183. chipgraph/packs/assist/triage/parse.py +371 -0
  184. chipgraph/packs/assist/triage/question.py +384 -0
  185. chipgraph/packs/assist/triage/report.py +160 -0
  186. chipgraph/packs/assist/triage/rules.py +447 -0
  187. chipgraph/packs/assist/triage/run.py +224 -0
  188. chipgraph/packs/digital_rtl/__init__.py +19 -0
  189. chipgraph/packs/digital_rtl/layout.py +144 -0
  190. chipgraph/packs/digital_rtl/pack.yml +25 -0
  191. chipgraph/packs/digital_rtl/plan/__init__.py +34 -0
  192. chipgraph/packs/digital_rtl/plan/__main__.py +16 -0
  193. chipgraph/packs/digital_rtl/plan/context.py +82 -0
  194. chipgraph/packs/digital_rtl/plan/expand.py +94 -0
  195. chipgraph/packs/digital_rtl/plan/model.py +150 -0
  196. chipgraph/packs/digital_rtl/plan/resolver.py +400 -0
  197. chipgraph/packs/digital_rtl/plan/show.py +176 -0
  198. chipgraph/packs/digital_rtl/pr/__init__.py +11 -0
  199. chipgraph/packs/digital_rtl/pr/body.py +165 -0
  200. chipgraph/packs/digital_rtl/pr/gate.py +110 -0
  201. chipgraph/packs/digital_rtl/pr/open.py +397 -0
  202. chipgraph/packs/digital_rtl/rtl/__init__.py +9 -0
  203. chipgraph/packs/digital_rtl/rtl/context.py +272 -0
  204. chipgraph/packs/digital_rtl/rules/open_pr.yml +30 -0
  205. chipgraph/packs/digital_rtl/rules/plan.yml +28 -0
  206. chipgraph/packs/digital_rtl/rules/plan_expand.yml +29 -0
  207. chipgraph/packs/digital_rtl/rules/pr_gate.yml +27 -0
  208. chipgraph/packs/digital_rtl/rules/review.yml +31 -0
  209. chipgraph/packs/digital_rtl/rules/rtl_module.yml +53 -0
  210. chipgraph/packs/digital_rtl/skills/lang-sv-rtl.md +58 -0
  211. chipgraph/packs/digital_rtl/skills/org-naming.md +29 -0
  212. chipgraph/packs/digital_rtl/skills/plan-modules.md +63 -0
  213. chipgraph/packs/digital_rtl/skills/review-diff.md +41 -0
  214. chipgraph/packs/digital_rtl/targets/plan.yml +11 -0
  215. chipgraph/packs/digital_rtl/targets/rtl.yml +20 -0
  216. chipgraph/packs/digital_rtl/wrapper/__init__.py +24 -0
  217. chipgraph/packs/digital_rtl/wrapper/common.py +171 -0
  218. chipgraph/packs/digital_rtl/wrapper/context.py +222 -0
  219. chipgraph/packs/digital_rtl/wrapper/emacs.py +626 -0
  220. chipgraph/packs/digital_rtl/wrapper/expansion.py +304 -0
  221. chipgraph/packs/digital_rtl/wrapper/lisp.py +504 -0
  222. chipgraph/packs/digital_rtl/wrapper/resolver.py +60 -0
  223. chipgraph/packs/digital_rtl/wrapper/rules/wrapper.yml +41 -0
  224. chipgraph/packs/digital_rtl/wrapper/rules/wrapper_gen.yml +35 -0
  225. chipgraph/packs/digital_rtl/wrapper/skills/wrapper-auto.md +120 -0
  226. chipgraph/packs/dv/__init__.py +15 -0
  227. chipgraph/packs/dv/declaration.py +392 -0
  228. chipgraph/packs/dv/interface.py +596 -0
  229. chipgraph/packs/dv/pack.yml +14 -0
  230. chipgraph/packs/dv/rules/tb_module.yml +42 -0
  231. chipgraph/packs/dv/skills/dv/cocotb.md +72 -0
  232. chipgraph/packs/spec_core/__init__.py +9 -0
  233. chipgraph/packs/spec_core/extract/__init__.py +12 -0
  234. chipgraph/packs/spec_core/extract/_markdown.py +371 -0
  235. chipgraph/packs/spec_core/extract/_text.py +116 -0
  236. chipgraph/packs/spec_core/extract/mas.py +975 -0
  237. chipgraph/packs/spec_core/gen/__init__.py +11 -0
  238. chipgraph/packs/spec_core/gen/agents_md/__init__.py +148 -0
  239. chipgraph/packs/spec_core/gen/agents_md/_context.py +239 -0
  240. chipgraph/packs/spec_core/gen/agents_md/_markers.py +131 -0
  241. chipgraph/packs/spec_core/gen/agents_md/agents_md.md.j2 +178 -0
  242. chipgraph/packs/spec_core/gen/diagram/__init__.py +110 -0
  243. chipgraph/packs/spec_core/gen/diagram/__main__.py +44 -0
  244. chipgraph/packs/spec_core/gen/diagram/_diagrams.py +403 -0
  245. chipgraph/packs/spec_core/gen/diagram/_scene.py +163 -0
  246. chipgraph/packs/spec_core/pack.yml +6 -0
  247. chipgraph/py.typed +0 -0
  248. chipgraph-0.1.0.dist-info/METADATA +53 -0
  249. chipgraph-0.1.0.dist-info/RECORD +252 -0
  250. chipgraph-0.1.0.dist-info/WHEEL +4 -0
  251. chipgraph-0.1.0.dist-info/entry_points.txt +61 -0
  252. chipgraph-0.1.0.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,310 @@
1
+ """The context of a task whose role must not see some artifacts (DESIGN.md 5.1, 5.3 F1).
2
+
3
+ `get_context` of runtime `claude-code` uses one of these for a task of a role with
4
+ `read_policy.deny_kinds` (`guarded_context` picks it): `GuardedContext` for a role that
5
+ must not see the RTL (the testbench Author), `RtlAuthorContext` for one that must not see
6
+ the testbench (the RTL Author). Both give the model inputs, the extra context sections
7
+ and the block's spec documents; the read policy itself (what is refused, what is
8
+ filtered) is the same engine for both (`chipgraph.core.runtime.roles.policy`).
9
+
10
+ **Not the RTL** (`GuardedContext`): its Design Model inputs are not the general model
11
+ slices, which name modules, RTL ports and RTL files, but
12
+
13
+ - ``block/<b>``: the block's spec side (`chipgraph.packs.dv.interface.spec_slice`):
14
+ requirements with their ids, registers and fields, interrupts, memory regions, clock
15
+ and reset;
16
+ - ``interface/<name>`` (a module, or a block for its top module): the interface from
17
+ `project_interface` (spec ports, else the model's RTL ports, else the module's
18
+ declaration only), also given as the context's `interface` section;
19
+ - anything else: a note, no content.
20
+
21
+ The context also gets a `requirements` section (every requirement of the task's block,
22
+ with its id), and the block's spec documents (the profile layout's `spec` paths) as
23
+ inputs when the rule does not list them. Nothing else derived from RTL goes in: no RTL
24
+ file, body text, line number or path.
25
+
26
+ **Not the testbench** (`RtlAuthorContext`, M2-04): the model input ``block/<b>`` is the
27
+ block's spec side with the requirements cut to the module's, ``interface/<module>`` points
28
+ to the `rtl` section, and the `rtl` section holds the module's approved plan entry, its
29
+ requirements, interface and dependencies, the naming rules as text and the style
30
+ exemplars (`chipgraph.packs.digital_rtl.rtl.context`). An exemplar the role must not read
31
+ is refused there. No other project file's text. A wrapper's task (a `wrapper` param,
32
+ M2-12) gets `rtl.wrapper` instead: the IP's port declaration, the wrapper's ports and the
33
+ naming rules (`chipgraph.packs.digital_rtl.wrapper.context`), never the IP's body.
34
+ """
35
+
36
+ from __future__ import annotations
37
+
38
+ import json
39
+ from collections.abc import Callable, Sequence
40
+ from typing import Any
41
+
42
+ from chipgraph.app.context import AppContext
43
+ from chipgraph.core.contracts import RuleInstance, RuleSpec
44
+ from chipgraph.core.model import DesignModel, ModelStore, ModelStoreError, default_model_db_path
45
+ from chipgraph.core.runtime.roles import RoleSpec
46
+ from chipgraph.packs.dv.interface import (
47
+ Interface,
48
+ block_requirements,
49
+ project_interface,
50
+ spec_slice,
51
+ )
52
+
53
+ TB_STATIC = "tb_static"
54
+ """The adapter id whose args (`top`, `files`, `filelist`) say where a module's declaration
55
+ is, so the context and the `tb_static` check see the same interface."""
56
+
57
+
58
+ def spec_documents(ctx: AppContext, block: str | None) -> list[str]:
59
+ """The block's spec documents that exist: the profile layout's `spec` paths."""
60
+ if block is None:
61
+ return []
62
+ value = ctx.require_profile().for_block(block).layout.get("spec")
63
+ templates = [value] if isinstance(value, str) else list(value or ())
64
+ found: list[str] = []
65
+ for template in templates:
66
+ rel = template.replace("{block}", block).replace("{BLOCK}", block.upper())
67
+ if "{" in rel:
68
+ continue
69
+ paths = sorted(ctx.root.glob(rel)) if set("*?[") & set(rel) else [ctx.root / rel]
70
+ for path in paths:
71
+ if path.is_file():
72
+ name = path.relative_to(ctx.root).as_posix()
73
+ if name not in found:
74
+ found.append(name)
75
+ return found
76
+
77
+
78
+ def _load_model(ctx: AppContext) -> DesignModel | None:
79
+ db = default_model_db_path(ctx.layout.root)
80
+ if not db.is_file():
81
+ return None
82
+ try:
83
+ return ModelStore(db).read()
84
+ except (ModelStoreError, OSError, ValueError):
85
+ return None
86
+
87
+
88
+ class GuardedContext:
89
+ """The model inputs and extra sections of one task that must not see RTL."""
90
+
91
+ instructions = (
92
+ " The ports you may use are in `interface` (every port names its `source`), "
93
+ "the block's requirements in `requirements`."
94
+ )
95
+ """What `get_context` adds to the task's instructions."""
96
+
97
+ def covers(self, path: str) -> str | None:
98
+ """The context section that already holds file input `path`, if any (none here)."""
99
+ return None
100
+
101
+ def __init__(self, ctx: AppContext, instance: RuleInstance) -> None:
102
+ self.ctx = ctx
103
+ self.instance = instance
104
+ self.block = instance.params.get("block")
105
+ self._model: DesignModel | None = None
106
+ self._loaded = False
107
+ self.interface: Interface | None = None
108
+
109
+ @property
110
+ def model(self) -> DesignModel | None:
111
+ """The ingested Design Model, or None when there is none yet."""
112
+ if not self._loaded:
113
+ self._loaded = True
114
+ self._model = _load_model(self.ctx)
115
+ return self._model
116
+
117
+ def _profile_parts(self, block: str | None) -> tuple[dict[str, Any], dict[str, Any]]:
118
+ resolved = self.ctx.require_profile()
119
+ profile = resolved.for_block(block) if block is not None else resolved.profile
120
+ cfg = profile.adapters.get(TB_STATIC)
121
+ args = cfg.model_dump() if cfg is not None else {}
122
+ return args, dict(profile.layout)
123
+
124
+ def _interface(self, name: str) -> Interface:
125
+ blocks = self.ctx.require_profile().profile.blocks
126
+ params = dict(self.instance.params)
127
+ block: str | None
128
+ if name in blocks or name == self.block:
129
+ block = name
130
+ else:
131
+ block = self.block
132
+ params["module"] = name
133
+ args, layout = self._profile_parts(block)
134
+ return project_interface(
135
+ self.ctx.root, model=self.model, block=block, params=params, args=args, layout=layout
136
+ )
137
+
138
+ def spec_documents(self) -> list[str]:
139
+ """The block's spec documents that exist: the profile layout's `spec` paths."""
140
+ return spec_documents(self.ctx, self.block)
141
+
142
+ def model_input(self, key: str) -> dict[str, Any]:
143
+ """The context entry of the model input `key`."""
144
+ entry: dict[str, Any] = {"model_key": key}
145
+ kind, _, name = key.partition("/")
146
+ if kind == "interface" and name:
147
+ self.interface = self._interface(name)
148
+ entry["section"] = "interface"
149
+ entry["note"] = "see the context's `interface` section"
150
+ return entry
151
+ if kind == "block" and name:
152
+ model = self.model
153
+ if model is None:
154
+ entry["note"] = "not available from the Design Model (run `chipgraph ingest` first)"
155
+ return entry
156
+ entry["content"] = json.dumps(spec_slice(model, name), indent=1, sort_keys=True)
157
+ return entry
158
+ entry["note"] = f"model input {key!r} is not available to this role"
159
+ return entry
160
+
161
+ def sections(self) -> dict[str, Any]:
162
+ """The extra context sections: `interface` (when an input asked for it) and
163
+ `requirements` (the block's, with ids)."""
164
+ out: dict[str, Any] = {}
165
+ if self.interface is not None:
166
+ out["interface"] = self.interface.view()
167
+ model = self.model
168
+ if model is not None and self.block is not None:
169
+ out["requirements"] = block_requirements(model, self.block)
170
+ return out
171
+
172
+
173
+ class RtlAuthorContext:
174
+ """The model inputs and extra sections of one task that must not see a testbench."""
175
+
176
+ instructions = (
177
+ " Everything about the module is in `rtl`: its approved plan entry (`rtl.module`), "
178
+ "its requirements with ids, its ports (`rtl.interface`, every port names its "
179
+ "`source`), the modules it connects to, the naming rules (`rtl.naming.text`) and "
180
+ "the style exemplars to imitate (`rtl.style`). The engine formats your file before "
181
+ "the other checks run."
182
+ )
183
+ """What `get_context` adds to the task's instructions."""
184
+
185
+ def __init__(
186
+ self,
187
+ ctx: AppContext,
188
+ instance: RuleInstance,
189
+ rules: Sequence[RuleSpec],
190
+ is_denied: Callable[[str], bool],
191
+ ) -> None:
192
+ self.ctx = ctx
193
+ self.instance = instance
194
+ self.rules = list(rules)
195
+ self.is_denied = is_denied
196
+ self.block = instance.params.get("block")
197
+ self.module = instance.params.get("module")
198
+ self.wrapper = instance.params.get("wrapper")
199
+ self.model = _load_model(ctx)
200
+ self._section: dict[str, Any] | None = None
201
+ if self.wrapper is not None: # a wrapper's source (M2-12): `rtl.wrapper`
202
+ from chipgraph.packs.digital_rtl.wrapper.context import INSTRUCTIONS
203
+
204
+ self.instructions = INSTRUCTIONS
205
+
206
+ def section(self) -> dict[str, Any]:
207
+ """The `rtl` section (built once)."""
208
+ if self._section is None:
209
+ from chipgraph.packs.digital_rtl.rtl.context import rtl_author_context
210
+
211
+ if self.block is None:
212
+ self._section = {"note": "this task has no block param"}
213
+ elif self.wrapper is not None:
214
+ from chipgraph.packs.digital_rtl.wrapper.context import wrapper_author_context
215
+
216
+ self._section = wrapper_author_context(
217
+ self.ctx.root,
218
+ self.ctx.require_profile(),
219
+ self.rules,
220
+ params=dict(self.instance.params),
221
+ outputs=[r.path for r in self.instance.outputs if r.path is not None],
222
+ model=self.model,
223
+ is_denied=self.is_denied,
224
+ )
225
+ else:
226
+ self._section = rtl_author_context(
227
+ self.ctx.root,
228
+ self.ctx.require_profile(),
229
+ self.rules,
230
+ block=self.block,
231
+ module=self.module,
232
+ model=self.model,
233
+ is_denied=self.is_denied,
234
+ )
235
+ return self._section
236
+
237
+ def spec_documents(self) -> list[str]:
238
+ """The block's spec documents that exist: the profile layout's `spec` paths."""
239
+ return spec_documents(self.ctx, self.block)
240
+
241
+ def covers(self, path: str) -> str | None:
242
+ """`rtl.style` / `rtl.naming` for a style input (an exemplar or the naming rules
243
+ file, which the resolver adds as inputs so an edit makes the module stale): the
244
+ section shows it, capped and labelled, so its text is not given twice."""
245
+ section = self.section()
246
+ style = section.get("style")
247
+ exemplars = style.get("exemplars", []) if isinstance(style, dict) else []
248
+ if any(isinstance(e, dict) and e.get("path") == path for e in exemplars):
249
+ return "rtl.style"
250
+ if self.block is not None:
251
+ from chipgraph.packs.digital_rtl.plan.resolver import style_inputs
252
+
253
+ if path in style_inputs(self.ctx, self.rules, self.block):
254
+ return "rtl.naming"
255
+ return None
256
+
257
+ def model_input(self, key: str) -> dict[str, Any]:
258
+ """The context entry of the model input `key`."""
259
+ from chipgraph.packs.digital_rtl.rtl.context import spec_side
260
+
261
+ entry: dict[str, Any] = {"model_key": key}
262
+ kind, _, name = key.partition("/")
263
+ if kind == "interface" and name and self.wrapper is not None:
264
+ entry["section"] = "rtl.wrapper.interface"
265
+ entry["note"] = "see the context's `rtl.wrapper.interface` and `rtl.wrapper.vendor`"
266
+ return entry
267
+ if kind == "interface" and name:
268
+ entry["section"] = "rtl.interface"
269
+ entry["note"] = "see the context's `rtl.interface` and `rtl.dependencies`"
270
+ return entry
271
+ if kind == "block" and name:
272
+ if self.model is None:
273
+ entry["note"] = "not available from the Design Model (run `chipgraph ingest` first)"
274
+ return entry
275
+ module = self.section().get("module")
276
+ reqs = list(module.get("reqs", ())) if isinstance(module, dict) else None
277
+ side = spec_side(self.model, name, reqs)
278
+ entry["content"] = json.dumps(side, indent=1, sort_keys=True)
279
+ return entry
280
+ entry["note"] = f"model input {key!r} is not available to this role"
281
+ return entry
282
+
283
+ def sections(self) -> dict[str, Any]:
284
+ """The extra context section: `rtl`."""
285
+ return {"rtl": self.section()}
286
+
287
+
288
+ def guarded_context(
289
+ ctx: AppContext,
290
+ role: RoleSpec | None,
291
+ instance: RuleInstance,
292
+ rules: Sequence[RuleSpec],
293
+ is_denied: Callable[[str], bool],
294
+ ) -> GuardedContext | RtlAuthorContext | None:
295
+ """The guarded context of a task whose role must not see some kinds, else None.
296
+
297
+ A role that must not see `rtl` gets `GuardedContext`; one that must not see `tb`
298
+ (and may see RTL) gets `RtlAuthorContext`.
299
+ """
300
+ if role is None or role.read_policy.mode != "deny" or not role.read_policy.deny_kinds:
301
+ return None
302
+ kinds = role.read_policy.deny_kinds
303
+ if "rtl" in kinds:
304
+ return GuardedContext(ctx, instance)
305
+ if "tb" in kinds:
306
+ return RtlAuthorContext(ctx, instance, rules, is_denied)
307
+ return None
308
+
309
+
310
+ __all__ = ["TB_STATIC", "GuardedContext", "RtlAuthorContext", "guarded_context", "spec_documents"]