Skip to content

Commit b1f257e

Browse files
committed
Align workspace shell and workflow docs
1 parent 3ce6344 commit b1f257e

5 files changed

Lines changed: 190 additions & 64 deletions

File tree

.github/workflows/pr-policy.yml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,23 +73,20 @@ jobs:
7373
/^\.github\/PULL_REQUEST_TEMPLATE\.md$/,
7474
/^\.github\/workflows\/pr-policy\.yml$/,
7575
/^CONTRIBUTING\.md$/,
76-
/^README\.md$/,
7776
];
7877
7978
const isDocsPolicyOnlyPr =
8079
changedFiles.length > 0 &&
8180
changedFiles.every((file) =>
8281
docsPolicyOnlyPatterns.some((pattern) => pattern.test(file.filename))
8382
);
84-
const isDocsAreaOnly = selectedAreas.length === 1 && selectedAreas[0] === "Docs";
85-
8683
const issuePattern = /\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?|refs?)\s+#\d+\b/i;
8784
const hasIssueReference = issuePattern.test(relatedIssueSection);
8885
const usesDocsOnlyException = noIssuePattern.test(relatedIssueSection);
8986
9087
if (!hasIssueReference) {
91-
if (!(usesDocsOnlyException && isDocsAreaOnly && isDocsPolicyOnlyPr)) {
92-
errors.push("`## Related Issue` must include an issue reference such as `Closes #12`, unless this is a Docs-only docs/policy PR and the section says `None (docs/policy-only PR)`.");
88+
if (!(usesDocsOnlyException && isDocsPolicyOnlyPr)) {
89+
errors.push("`## Related Issue` must include an issue reference such as `Closes #12`, unless changed files are limited to the docs/policy paths and the section says `None (docs/policy-only PR)`.");
9390
}
9491
}
9592

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ PR 제목은 아래 형식을 따릅니다.
6767
- 아키텍처 규칙 점검 결과
6868
- 빌드/테스트 검증 결과 또는 미실행 사유
6969
- 남은 리스크나 후속 작업
70+
- docs/policy-only PR이고 변경 경로가 `docs/`, `uml/`, `CONTRIBUTING.md`, PR/issue template, `pr-policy.yml`에만 한정되면 `Related Issue`에는 `None (docs/policy-only PR)`를 사용합니다.
7071

7172
## 아키텍처 체크
7273

docs/process/GitHub Project.md

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,35 @@
1010
- 선행 작업 관계는 GitHub native issue dependency `blocked by`를 사용한다.
1111
- Task 제목은 `Task-short title` 형식을 사용하고, 순서는 제목 번호가 아니라 GitHub issue 번호, Sprint, Parent issue, dependency로 관리한다.
1212

13-
## 현재 필드
14-
- `Title`
15-
- `Status`
16-
- `In Progress`
17-
- `Done`
18-
- `Area`
19-
- `⚙️Engine`
20-
- `🏃Domain`
21-
- `🖥️Application`
22-
- `🔎Analysis`
23-
- `📄Docs`
24-
- 메모: issue form은 `Build`도 받지만, 현재 Project `Area` 필드에는 `Build` 옵션이 없다.
25-
- `Sprint`
26-
- `Sprint 1`
27-
- `Sprint 2`
28-
- `Sprint 3`
29-
- `Later`
30-
- `Parent issue`
31-
- `Sub-issues progress`
13+
## 실제 필드
14+
- 기본 컨텍스트 필드
15+
- `Title`
16+
- `Assignees`
17+
- `Labels`
18+
- `Linked pull requests`
19+
- `Milestone`
20+
- `Repository`
21+
- `Reviewers`
22+
- 작업 추적 필드
23+
- `Status`
24+
- `In Progress`
25+
- `Done`
26+
- `Area`
27+
- `⚙️Engine`
28+
- `🏃Domain`
29+
- `🖥️Application`
30+
- `🔎Analysis`
31+
- `📄Docs`
32+
- `Sprint`
33+
- `Sprint 1`
34+
- `Sprint 2`
35+
- `Sprint 3`
36+
- `Later`
37+
- `Parent issue`
38+
- `Sub-issues progress`
39+
- 메모
40+
- issue form은 `Build`도 받지만, 현재 Project `Area` 필드에는 `Build` 옵션이 없다.
41+
- `Build` 성격 작업은 issue 본문에는 `Build`로 남기고, 보드에서는 가장 가까운 기존 영역으로 배치한다.
3242

3343
## 현재 뷰
3444
- 뷰 수: 1개
@@ -44,19 +54,18 @@
4454
- `Parent issue`
4555
- `Sub-issues progress`
4656

47-
## 현재 구조
57+
## 현재 상위 구조
4858
- `Sprint 1`
4959
- Epic: `#1 EPIC-1 Engine Foundation`
5060
- Epic: `#2 EPIC-2 Sprint 1 Demo Vertical Slice`
51-
- Engine foundation tasks: `#6 ~ #13`, `#47`
52-
- Demo vertical slice tasks: `#14 ~ #20`, `#52 ~ #55`
61+
- Epic: `#3 EPIC-3 MVP 코어 구조 정렬`
5362
- `Sprint 2`
54-
- Epic: `#3 EPIC-3 Product Completion for Sprint 2`
5563
- Epic: `#4 EPIC-4 Compare and Presentation Readiness`
56-
- Task: `#21 ~ #30` (`Task-...` 형식)
5764
- `Sprint 3`
58-
- Epic: `#5 EPIC-5 Finish and Optional Extensions`
59-
- Task: `#31 ~ #35` (`Task-...` 형식)
65+
- Epic: `#5 EPIC-5 1차 확장 기능`
66+
- `Later`
67+
- Epic: `#97 EPIC-중기 확장 기능`
68+
- Epic: `#98 EPIC-연구 후보 검토`
6069

6170
## 메모
6271
- 작업을 시작하기 전에 먼저 관련 issue가 이미 있는지 확인한다.
@@ -65,11 +74,10 @@
6574
- Project view에서 `Parent issue`, `Sub-issues progress` 컬럼으로 연결 결과가 보이는지 확인한다.
6675
- `Docs`, `Chore`, `Analysis``Lightweight Task` form으로 가볍게 등록한다.
6776
- `Engine`, `Domain`, `Application`, `Build``Implementation Task` form으로 범위와 검증 계획까지 남긴다.
68-
- 현재 Project 보드의 `Area` 필드에는 `Build` 옵션이 없으므로, Build 성격 task는 issue form과 본문에는 `Build`로 남기고 보드에서는 임시로 가장 가까운 기존 영역에 배치한다.
77+
- docs/policy-only 예외는 변경 경로가 `docs/`, `uml/`, `CONTRIBUTING.md`, PR/issue template, `pr-policy.yml`에만 한정될 때만 사용한다.
78+
- docs/policy-only PR의 `Related Issue` 섹션은 `None (docs/policy-only PR)`로 남긴다.
6979
- 세부 작업명, 부모-자식 관계, dependency는 GitHub Project와 issue 자체를 기준으로 관리한다.
7080
- `blocked by`는 실제로 선행 해결이 필요한 hard dependency에만 건다. 단순한 권장 순서나 같은 Epic 안의 묶음 관계 때문에 불필요하게 직렬화하지 않는다.
7181
- 하나의 Task가 서로 다른 관심사를 함께 묶어 병렬 진행을 막으면, 별도 Task로 분리해서 dependency를 다시 연결한다.
7282
- 문서 또는 기여 정책만 다루는 변경은 별도 issue 없이 진행할 수 있다.
73-
- 변경 범위가 `docs/`, `uml/`, `CONTRIBUTING.md`, PR/issue template, PR 정책 워크플로에만 한정되면 유지보수자는 PR 없이 `main`에 직접 push할 수 있다.
7483
- Task의 순서는 제목 접두사 뒤 숫자로 관리하지 않는다. 중간 작업이 생기면 새 issue를 추가하고 `Sprint`, `Parent issue`, `blocked by`로 위치를 표현한다.
75-
- `#23 Task-Implement drawing import or preprocessing to FacilityLayout2D flow`는 범위가 넓어서 삭제했고, Sprint 1 데모용 import 흐름은 `#52 ~ #55`로 분리했다.

src/application/MainWindow.cpp

Lines changed: 139 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#include "application/MainWindow.h"
22

3+
#include <QFormLayout>
4+
#include <QGroupBox>
5+
#include <QHBoxLayout>
36
#include <QLabel>
47
#include <QPushButton>
58
#include <QTimer>
@@ -28,6 +31,19 @@ QString stateToString(safecrowd::engine::EngineState state) {
2831
return "Unknown";
2932
}
3033

34+
QLabel* createBodyLabel(const QString& text, QWidget* parent) {
35+
auto* label = new QLabel(text, parent);
36+
label->setWordWrap(true);
37+
label->setTextFormat(Qt::RichText);
38+
return label;
39+
}
40+
41+
QLabel* createValueLabel(QWidget* parent) {
42+
auto* label = new QLabel("-", parent);
43+
label->setTextInteractionFlags(Qt::TextSelectableByMouse);
44+
return label;
45+
}
46+
3147
} // namespace
3248

3349
namespace safecrowd::application {
@@ -36,64 +52,159 @@ MainWindow::MainWindow(safecrowd::domain::SafeCrowdDomain& domain, QWidget* pare
3652
: QMainWindow(parent),
3753
domain_(domain) {
3854
auto* centralWidget = new QWidget(this);
39-
auto* layout = new QVBoxLayout(centralWidget);
40-
statusLabel_ = new QLabel(this);
41-
42-
auto* startButton = new QPushButton("Start", this);
43-
auto* pauseButton = new QPushButton("Pause", this);
44-
auto* stopButton = new QPushButton("Stop", this);
45-
46-
layout->addWidget(statusLabel_);
47-
layout->addWidget(startButton);
48-
layout->addWidget(pauseButton);
49-
layout->addWidget(stopButton);
55+
auto* rootLayout = new QHBoxLayout(centralWidget);
56+
rootLayout->setContentsMargins(18, 18, 18, 18);
57+
rootLayout->setSpacing(16);
58+
59+
auto* workspaceGroup = new QGroupBox("Project Workspace", centralWidget);
60+
auto* workspaceLayout = new QVBoxLayout(workspaceGroup);
61+
workspaceLayout->setSpacing(12);
62+
workspaceLayout->addWidget(createBodyLabel(
63+
"<b>1. Import &amp; Validate</b><br/>"
64+
"DXF and facility topology import, review, and manual correction will surface here.",
65+
workspaceGroup));
66+
workspaceLayout->addWidget(createBodyLabel(
67+
"<b>2. Scenario Editor</b><br/>"
68+
"Baseline and variation authoring stay in the same workspace but outside the run panel.",
69+
workspaceGroup));
70+
workspaceLayout->addWidget(createBodyLabel(
71+
"<b>3. Results &amp; Recommendation</b><br/>"
72+
"Run summaries, comparison, export, and recommendation remain downstream of persisted artifacts.",
73+
workspaceGroup));
74+
workspaceLayout->addStretch();
75+
76+
auto* workspaceColumn = new QVBoxLayout();
77+
workspaceColumn->setSpacing(16);
78+
workspaceColumn->addWidget(createBodyLabel(
79+
"<b>SafeCrowd Workspace Prototype</b><br/>"
80+
"This shell now mirrors the documented workflow. Only playback control is wired live today; "
81+
"the rest of the workspace is reserved so future application features land in explicit sections.",
82+
centralWidget));
83+
84+
auto* runControlGroup = new QGroupBox("Run Control Panel", centralWidget);
85+
auto* runControlLayout = new QVBoxLayout(runControlGroup);
86+
runControlLayout->setSpacing(10);
87+
runControlLayout->addWidget(createBodyLabel(
88+
"Playback control remains the active path into the current runtime prototype.",
89+
runControlGroup));
90+
91+
auto* buttonLayout = new QHBoxLayout();
92+
startButton_ = new QPushButton("Start Playback", runControlGroup);
93+
pauseButton_ = new QPushButton("Pause Playback", runControlGroup);
94+
stopButton_ = new QPushButton("Stop Playback", runControlGroup);
95+
buttonLayout->addWidget(startButton_);
96+
buttonLayout->addWidget(pauseButton_);
97+
buttonLayout->addWidget(stopButton_);
98+
runControlLayout->addLayout(buttonLayout);
99+
runControlLayout->addWidget(createBodyLabel(
100+
"<b>Planned next:</b> execution readiness checks, repeat runs, and variation selection.",
101+
runControlGroup));
102+
103+
auto* runtimeStatusGroup = new QGroupBox("Runtime Status", centralWidget);
104+
auto* runtimeStatusLayout = new QFormLayout(runtimeStatusGroup);
105+
runtimeStatusLayout->setLabelAlignment(Qt::AlignLeft);
106+
runtimeStatusLayout->setFormAlignment(Qt::AlignTop | Qt::AlignLeft);
107+
108+
runtimeStateValue_ = createValueLabel(runtimeStatusGroup);
109+
frameValue_ = createValueLabel(runtimeStatusGroup);
110+
fixedStepValue_ = createValueLabel(runtimeStatusGroup);
111+
alphaValue_ = createValueLabel(runtimeStatusGroup);
112+
runValue_ = createValueLabel(runtimeStatusGroup);
113+
variationValue_ = createValueLabel(runtimeStatusGroup);
114+
115+
runtimeStatusLayout->addRow("Engine state", runtimeStateValue_);
116+
runtimeStatusLayout->addRow("Rendered frames", frameValue_);
117+
runtimeStatusLayout->addRow("Fixed steps", fixedStepValue_);
118+
runtimeStatusLayout->addRow("Interpolation alpha", alphaValue_);
119+
runtimeStatusLayout->addRow("Current run", runValue_);
120+
runtimeStatusLayout->addRow("Variation", variationValue_);
121+
122+
auto* resultsGroup = new QGroupBox("Results Pipeline", centralWidget);
123+
auto* resultsLayout = new QVBoxLayout(resultsGroup);
124+
resultsLayout->setSpacing(12);
125+
resultsLayout->addWidget(createBodyLabel(
126+
"<b>Run Results Panel</b><br/>"
127+
"Single-run and variation summaries will read persisted artifacts first.",
128+
resultsGroup));
129+
resultsLayout->addWidget(createBodyLabel(
130+
"<b>Comparison View</b><br/>"
131+
"Baseline versus alternative comparisons stay separate from live runtime state.",
132+
resultsGroup));
133+
resultsLayout->addWidget(createBodyLabel(
134+
"<b>Export &amp; Recommendation</b><br/>"
135+
"Artifact export and recommendation evidence remain downstream consumers of saved results.",
136+
resultsGroup));
137+
138+
workspaceColumn->addWidget(runControlGroup);
139+
workspaceColumn->addWidget(runtimeStatusGroup);
140+
workspaceColumn->addWidget(resultsGroup);
141+
workspaceColumn->addStretch();
142+
143+
rootLayout->addWidget(workspaceGroup, 5);
144+
rootLayout->addLayout(workspaceColumn, 7);
50145

51146
tickTimer_ = new QTimer(this);
52147
tickTimer_->setInterval(16);
53148

54-
connect(startButton, &QPushButton::clicked, this, [this]() { startSimulation(); });
55-
connect(pauseButton, &QPushButton::clicked, this, [this]() { pauseSimulation(); });
56-
connect(stopButton, &QPushButton::clicked, this, [this]() { stopSimulation(); });
149+
connect(startButton_, &QPushButton::clicked, this, [this]() { startSimulation(); });
150+
connect(pauseButton_, &QPushButton::clicked, this, [this]() { pauseSimulation(); });
151+
connect(stopButton_, &QPushButton::clicked, this, [this]() { stopSimulation(); });
57152
connect(tickTimer_, &QTimer::timeout, this, [this]() { tickSimulation(); });
58153

59154
setCentralWidget(centralWidget);
60-
setWindowTitle("SafeCrowd");
61-
resize(420, 220);
155+
setWindowTitle("SafeCrowd Workspace");
156+
resize(980, 560);
62157

63-
refreshStatusLabel();
158+
refreshRuntimePanel();
64159
}
65160

66161
void MainWindow::startSimulation() {
67162
domain_.start();
68163
tickTimer_->start();
69-
refreshStatusLabel();
164+
refreshRuntimePanel();
70165
}
71166

72167
void MainWindow::pauseSimulation() {
73168
domain_.pause();
74169
tickTimer_->stop();
75-
refreshStatusLabel();
170+
refreshRuntimePanel();
76171
}
77172

78173
void MainWindow::stopSimulation() {
79174
domain_.stop();
80175
tickTimer_->stop();
81-
refreshStatusLabel();
176+
refreshRuntimePanel();
82177
}
83178

84179
void MainWindow::tickSimulation() {
85180
domain_.update(1.0 / 60.0);
86-
refreshStatusLabel();
181+
refreshRuntimePanel();
87182
}
88183

89-
void MainWindow::refreshStatusLabel() {
184+
void MainWindow::refreshRuntimePanel() {
185+
using safecrowd::engine::EngineState;
186+
90187
const auto summary = domain_.summary();
91-
statusLabel_->setText(
92-
QString("State: %1\nFrames: %2\nFixed Steps: %3\nAlpha: %4")
93-
.arg(stateToString(summary.state))
94-
.arg(summary.frameIndex)
95-
.arg(summary.fixedStepIndex)
96-
.arg(summary.alpha, 0, 'f', 2));
188+
runtimeStateValue_->setText(stateToString(summary.state));
189+
frameValue_->setText(QString::number(summary.frameIndex));
190+
fixedStepValue_->setText(QString::number(summary.fixedStepIndex));
191+
alphaValue_->setText(QString::number(summary.alpha, 'f', 2));
192+
193+
if (summary.state == EngineState::Running || summary.state == EngineState::Paused) {
194+
runValue_->setText("Prototype run 1 / 1");
195+
} else if (summary.frameIndex > 0 || summary.fixedStepIndex > 0) {
196+
runValue_->setText("Last prototype run retained");
197+
} else {
198+
runValue_->setText("Ready for first run");
199+
}
200+
201+
variationValue_->setText("Baseline placeholder (domain wiring pending)");
202+
203+
const bool isRunning = summary.state == EngineState::Running;
204+
const bool isPaused = summary.state == EngineState::Paused;
205+
startButton_->setEnabled(!isRunning);
206+
pauseButton_->setEnabled(isRunning);
207+
stopButton_->setEnabled(isRunning || isPaused || summary.frameIndex > 0 || summary.fixedStepIndex > 0);
97208
}
98209

99210
} // namespace safecrowd::application

src/application/MainWindow.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class SafeCrowdDomain;
77
}
88

99
class QLabel;
10+
class QPushButton;
1011
class QTimer;
1112

1213
namespace safecrowd::application {
@@ -20,10 +21,18 @@ class MainWindow : public QMainWindow {
2021
void pauseSimulation();
2122
void stopSimulation();
2223
void tickSimulation();
23-
void refreshStatusLabel();
24+
void refreshRuntimePanel();
2425

2526
safecrowd::domain::SafeCrowdDomain& domain_;
26-
QLabel* statusLabel_{nullptr};
27+
QPushButton* startButton_{nullptr};
28+
QPushButton* pauseButton_{nullptr};
29+
QPushButton* stopButton_{nullptr};
30+
QLabel* runtimeStateValue_{nullptr};
31+
QLabel* frameValue_{nullptr};
32+
QLabel* fixedStepValue_{nullptr};
33+
QLabel* alphaValue_{nullptr};
34+
QLabel* runValue_{nullptr};
35+
QLabel* variationValue_{nullptr};
2736
QTimer* tickTimer_{nullptr};
2837
};
2938

0 commit comments

Comments
 (0)